# Sessions

> How the SDK authenticates and establishes a live voice session: the control-plane token exchange, the relay session, and namespace auth.

A **session** is the live transport connection that carries one call's audio.
Your code holds the session open while a caller and an agent talk. The session
is a [MoQT](/modalities/voice/transport-web/webtransport) session over
WebTransport. A relay token authenticates the session. The session's scope is a
room-shaped namespace keyed on the `call_sid`.

This page documents how the SDK authenticates and brings up that session. This
is the plumbing under
[`AudioBridge.attach`](/modalities/voice/api/transport). You rarely wire it by
hand, but you need the shapes to reason about auth, tenancy, and reconnection.

> **NOTE:**
> There is **no public REST API for sessions.** The control plane is a set of
> tRPC-style procedures on the control-plane host. The SDK calls these procedures
> *for you*. You authenticate with an API key. The SDK does the procedure calls
> and the relay handshake. The shapes below are the real shapes that the SDK
> sends. Treat them as the contract, not as endpoints to hand-roll.

## Two planes, one credential

A session touches two planes. The same workspace API key authorizes both:

| Plane | What it does | Transport | Auth |
| --- | --- | --- | --- |
| **Control plane** | Mints call state, resolves the relay edge, authorizes the tenant | tRPC over HTTPS to `portal.firstbyte.fast` | `Authorization: Bearer <apiKey>` |
| **Data plane** | Carries the encoded audio for the call | MoQT over WebTransport to `relay.firstbyte.fast` | Relay token (`?token=`) + namespace scope |

The control plane is a request/response surface (queries and mutations). The
data plane is a long-lived session that auto-reconnects. To get onto the data
plane, first ask the control plane where the relay lives. The control plane
also proves that you can attach.

## Establish a session

  1. **Authenticate the control plane**
Construct the client with your workspace API key and org id. The SDK sends
    the key as `Authorization: Bearer <apiKey>` on every control-plane call.
    The org id scopes every procedure to your tenant.

    
    **TypeScript:**
```ts
    import { Voice } from "clutchcall_connect/voice";

    const voice = new Voice({
      baseUrl: "https://portal.firstbyte.fast",
      apiKey:  process.env.CLUTCHCALL_API_KEY!,
      orgId:   "org_123",
      relayHost: "relay.firstbyte.fast",   // optional; this is the default
    });
    ```

    **Python:**
```python
    import os
    from clutchcall.voice import Voice

    voice = Voice(
        base_url="https://portal.firstbyte.fast",
        api_key=os.environ["CLUTCHCALL_API_KEY"],
        org_id="org_123",
        relay_host="relay.firstbyte.fast",   # optional; this is the default
    )
    ```

  2. **Resolve the relay edge**
Before the SDK opens a session, it asks the control plane which relay edge
    to dial. This is a **query**. The SDK sends it, but the shape is worth
    knowing. The query geo-routes you to the nearest edge. It also carries the
    fields that the WebTransport handshake needs.

    - **`orgId`** (`string`, required) — Your tenant id. It scopes the response to your workspace.
    - **`relayRegion`** (`string`) — Pin a specific relay edge (e.g. `"us"`, `"uk"`). Omit this field to let       the server geo-route to the nearest edge.

    Response:

    - **`enabled`** (`boolean`) — `false` when the tenant has no QUIC/relay entitlement. The rest of the       fields are then absent. Fall back to a control-plane-only integration.
    - **`relayUrl`** (`string`) — The MoQT relay endpoint to open the session against. The server already       selected the region.
    - **`relayRegion`** (`string`) — The edge that the server chose. The response echoes it for telemetry and       for a pin on reconnect.
    - **`relayCertHashBase64`** (`string | null`) — A base64 SHA-256 certificate fingerprint for self-signed **dev** relays.       This is `null` in production. In production, the relay presents a       public-CA certificate, and no fingerprint pin is necessary.

    > **NOTE:**
> On managed cloud, the relay runs on a public-CA certificate, so
>     `relayCertHashBase64` is `null`. You pass a cert hash only when you point
>     the SDK at a self-signed relay in local development.

  3. **Open the relay session**
With the relay URL and a token, the SDK opens the MoQT session. Under the
    hood this is `MoqtClient.connect(url, token, onState, opts)`. The SDK
    appends the token to the URL as a `?token=` query. The relay's control
    plane verifies the token. The WebTransport handshake advertises the
    `moqt-16` subprotocol. The relay closes the session if the version
    subprotocol is missing.

    ```ts
    // What AudioBridge.attach does for you — shown for reference.
    const bridge = await voice.audioBridge.attach(call.sid, {
      codec: "opus",
      onUplink: (frame, tsUs) => { /* caller audio in */ },
    });
    // -> connects moq://relay.firstbyte.fast/voice/<call_sid>
    // -> subscribes voice/<call_sid>/uplink, publishes voice/<call_sid>/downlink
    ```

    The session address is the `call_sid`. The SDK connects to
    `moq://<relayHost>/voice/<call_sid>` and binds the `uplink` / `downlink`
    tracks under that namespace. One `call_sid` is the whole addressing story.
    The SIP dialog, the media session, the agent session, the telemetry, and
    this transport session all key off it (see
    [Sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams)).

  4. **Observe session state**
The session reconnects transparently with capped backoff. The SDK
    re-establishes publications and subscriptions on every reconnect. Subscribe
    to the state machine to drive UI and to know when audio actually flows.

    ```ts
    // ConnectionState, shared across every SDK.
    // Connecting → Connected → (Reconnecting ⇄ Connected)* → Closed | Failed
    ```

    - **`Connecting`** — The first connect is in progress.
    - **`Connected`** — The session is up; the tracks are live.
    - **`Reconnecting`** — The transport dropped. The SDK retries with capped exponential backoff.       On success, the SDK replays your pubs/subs.
    - **`Closed`** — You (or the peer) ended the session cleanly.
    - **`Failed`** — The initial connect failed. The state carries a reason string.

## Namespace auth (room scope)

The relay does not give a token holder the whole tenant. Authorization is
**namespace-scoped**: a session can publish and subscribe only within the
room-shaped namespace tuple that the relay authorized it for. For voice, that
room is `voice/<call_sid>`. The two tracks inside it are fixed by role:

| Track | Direction | Who publishes |
| --- | --- | --- |
| `voice/<call_sid>/uplink` | caller → cloud | the caller (or the media plane) |
| `voice/<call_sid>/downlink` | cloud → caller | the agent / your bridge |

The distinct uplink and downlink tracks prevent a leg from hearing itself. An
`attach` subscribes to uplink and publishes downlink. `attachCaller` (the
browser-caller mirror) does the reverse.

> **WARNING:**
> Namespaces are **tuples with a room scope**, never a single flat identifier. A
> valid namespace carries at least a one-element prefix *and* a suffix. The relay
> rejects a zero-element namespace. This is why every voice session address is
> `voice/<call_sid>/<track>` and never a bare id: the relay authorizes against
> the room prefix.

## Connect options

These are the knobs that the SDK passes to the relay session. `attach` sets the
media-relevant knobs for you. These knobs are the honest surface if you drop to
the transport client directly.

- **`token`** (`string`) — The relay token. The relay control plane verifies it. The SDK sends it as a   `?token=` query on the WebTransport URL. The Voice SDK forwards your   workspace API key here.
- **`serverCertificateHash`** (`string`) — A base64 SHA-256 certificate fingerprint for a self-signed **dev** relay.   Leave this unset in production (`relayCertHashBase64` from   `gatewayConnectInfo` is `null` there).
- **`onState`** (`(state, reason?) => void`) — A callback for the `ConnectionState` machine above.
- **`requireEncodedTransform`** (`boolean`) — Refuse to connect unless the browser exposes the standard WebRTC Encoded   Transform API (`RTCRtpScriptTransform`). Media-sending browser sessions set   this flag, so the encoded-frame rule holds up front instead of failing later   at capture time. Leave the flag unset for receive-only or non-media sessions.   See   [browser audio capture](/modalities/voice/transport-web/browser-audio-capture).

> **NOTE:**
> **Fallback honesty.** In the browser / TypeScript SDK, a session is
> WebTransport-only today. If WebTransport is unavailable, the SDK does not
> silently degrade. The QUIC→WebSocket fallback ladder currently ships only in
> the native SDK core (Python, Go, Rust, Java, .NET wrappers). The native core
> stays on a WebSocket rung when QUIC is blocked.

## Sessions vs. calls

Keep the two concepts clearly apart:

- A **call** is the SIP dialog and its lifecycle — originate, ring, answer,
  transfer, hangup. You drive a call with the
  [Calls API](/modalities/voice/api/calls) (`voice.calls.*`), which returns
  `CallData` keyed by `sid`.
- A **session** is the transport connection that carries that call's audio. The
  same `sid` addresses it. You open a session with the
  [Transport / AudioBridge API](/modalities/voice/api/transport).

The typical flow:

1. Originate a call (control plane).
2. Take the returned `sid`.
3. Attach a session (data plane) to move audio.

For the server-side "drop-in agent" pattern, you skip the manual session
entirely. `voice.agents.attach` tells the engine to connect the bridge
in-process, and no client session opens. See the
[runtime session lifecycle](/modalities/voice/runtime/session-lifecycle) for
the engine's view of that flow.

## Related

  - **[Transport / AudioBridge](/modalities/voice/api/transport)** — Open and drive the audio session for a call.
  - **[Calls](/modalities/voice/api/calls)** — Originate, transfer, and hang up — the call control plane.
  - **[Sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams)** — The object model: how `call_sid`, namespaces, and tracks relate.
  - **[Errors](/modalities/voice/api/errors)** — The error shapes that the control plane and the session return.
