# From Twilio Media Streams

> Move a Twilio Programmable Voice / Media Streams integration onto QUIC. This page shows what ships today and the Media Streams adapter on the roadmap.

Bring calls that you run on Twilio into FirstByte Voice. This page is honest
about status. A **drop-in Twilio Media Streams adapter** — one that terminates
Twilio's `<Stream>` TwiML protocol and its JSON media envelope natively — is
**not shipped yet**. But two on-ramps carry Twilio traffic **today**. The Media
Streams adapter, when it lands, reuses the same 8 kHz µ-law WebSocket shape
that those on-ramps already use.

> **WARNING:**
> **No native `<Stream>` adapter yet (preview).** FirstByte does not yet
>   terminate Twilio's Media Streams protocol — the `<Connect><Stream>` handshake
>   and its `connected` / `start` / `media` / `mark` / `stop` JSON events — as a
>   turnkey endpoint. If you point a Twilio `<Stream>` straight at the platform,
>   the platform will not understand it. Use one of the
>   [two paths that work today](#what-works-today). Everything under
>   [the intended adapter](#the-intended-media-streams-adapter-roadmap) is
>   roadmap and can change.

## Pick the right on-ramp

Twilio users choose Media Streams for one of two reasons. The better
FirstByte path depends on your reason:

  - **You want an AI agent on the call** — You stream audio to your own model today only because Twilio cannot run the
    agent for you. On FirstByte the agent runs **in the engine**. Thus you
    do not need Media Streams at all. Bring the **SIP trunk** and let the
    runtime handle the media. This is the recommended, fully shipped route.
  - **You keep your own media processing** — You genuinely need raw audio frames delivered to your own service over a
    WebSocket (custom DSP, a bespoke pipeline, an external orchestrator). Use
    the **custom WebSocket audio** on-ramp. Translate Twilio's envelope with a
    thin shim.

## What works today

### Option A — bring the Twilio SIP trunk (recommended)

Twilio Elastic SIP Trunking terminates on your workspace's own SIP endpoint.
Media Streams is not involved. Point Twilio at
`<workspace-id>.sip.firstbyte.fast`. Calls then flow straight into the runtime:
AI agents, IVR flows, and human queues. The phone leg stays G.711 µ-law on the
wire.

  1. **Create a trunk in FirstByte**
Add a **bring-your-own** trunk that points back at Twilio. Match it by
    Twilio's signalling source IPs. See
    [SIP trunking](/modalities/voice/transport-telephony/sip-trunking)
    for every field.
  2. **Point Twilio's Origination URI at your endpoint**
In the Twilio console, set the trunk's Origination URI to
    `sip:<workspace-id>.sip.firstbyte.fast`. Inbound calls from a configured
    trunk peer bypass the workspace registrar ACL. Thus Twilio does not need to
    register.
  3. **Route the inbound calls**
Set the trunk's inbound rule to **Handle with AI**. Or bind it to a skill /
    VDN for an IVR/ACD flow. See
    [Inbound calls](/modalities/voice/transport-telephony/inbound).

> **NOTE:**
> The wire codec stays G.711 µ-law on both sides. Thus there is **no
>   transcode** on a straight carrier hand-off. See the
>   [passthrough fast paths](/modalities/voice/transport-telephony/codecs#passthrough-fast-paths-zero-transcode).
>   The runtime only transcodes to its internal audio bus when a model actually
>   needs PCM.

### Option B — custom WebSocket audio + a Twilio envelope shim

You may have to keep the WebSocket-audio shape. For that case, FirstByte
exposes a **custom WebSocket audio on-ramp** for bespoke senders that stream
raw framed audio. It is *codec-agnostic at the µ-law layer*: 8 kHz µ-law frames
map straight onto a PCMU SIP trunk with zero transcode. This is the same shape
that Twilio's `media` events carry.

The on-ramp does **not** speak Twilio's JSON envelope. So today you run a small
shim that:

1. Accepts Twilio's `<Stream>` WebSocket connection.
2. Unwraps each `media` event — base64-decodes `media.payload` back to raw 8 kHz
   µ-law.
3. Sends the raw µ-law frames to the FirstByte WebSocket audio on-ramp,
   keyed by `call_sid`.

The Twilio side of that shim is Twilio's own documented protocol. You receive
events like these from Twilio:

```json
{ "event": "start",  "start": { "streamSid": "MZ…", "mediaFormat": { "encoding": "audio/x-mulaw", "sampleRate": 8000, "channels": 1 } } }
{ "event": "media",  "media": { "track": "inbound", "chunk": "1", "timestamp": "5", "payload": "<base64 µ-law>" } }
{ "event": "stop",   "stop":  { "callSid": "CA…", "streamSid": "MZ…" } }
```

Your shim decodes `media.payload` and writes the bytes to the FirstByte WS
audio bridge. See
[Migrate from custom WebSocket audio](/modalities/voice/migration/from-websocket-audio)
for the on-ramp's frame contract. The same µ-law bytes flow. You only strip
Twilio's wrapper first.

> **NOTE:**
> This is the same WebSocket-audio ingress that vendor bridges (for example to
>   LiveKit or Vapi) use. You may want to route the media to an external
>   agent-orchestration vendor instead of the in-engine runtime. That path is
>   described under
>   [custom runtime & vendor bridge](/modalities/voice/integrations/custom-runtime).

## The intended Media Streams adapter (roadmap)

The planned adapter removes the shim. You point Twilio's `<Stream>` at a
FirstByte endpoint, and no other work is needed. The concept — **this is the
intended shape, not shipped behavior**:

  1.
` terminator">
    A WebSocket endpoint that speaks Twilio's protocol directly. It accepts the
    `connected` / `start` / `media` / `mark` / `stop` events and answers with
    `mark` / `clear`. Your TwiML is then only
    `<Connect></Connect>`.
  2. **streamSid → call_sid mapping**
The adapter maps Twilio's `streamSid` / `callSid` onto the FirstByte
    `call_sid`. Thus the session, agent, recording, and telemetry all key off
    one identifier. See
    [Sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams).
  3. **µ-law passthrough, zero transcode**
Twilio streams 8 kHz µ-law, and the phone plane is PCMU. Thus inbound
    `media` payloads land on a PCMU leg **without** re-encoding. This codec
    fast path already exists.
  4. **mark / clear for barge-in**
Outbound audio carries Twilio `mark` events, so your app tracks playback
    boundaries. The adapter honors `clear` to flush buffered audio on barge-in.
    This maps onto the runtime's turn-taking and interruption model.

> **WARNING:**
> Do not build against the adapter yet. There is no `<Stream>` endpoint URL to
>   target. The shape above can change before it ships. If a native Media Streams
>   adapter is on your critical path, tell your FirstByte team. The team can
>   then prioritize it and scope it against your exact TwiML.

## Status

| Path | Status |
|------|--------|
| Twilio SIP trunk → workspace SIP endpoint | Available — recommended for AI-on-call |
| µ-law → PCMU codec passthrough (zero transcode) | Available |
| Custom WebSocket audio on-ramp + your envelope shim | Available — you write the shim |
| Native Twilio `<Stream>` adapter (no shim) | Roadmap — not shipped |
| Twilio as an external vendor bridge target | Available (external sidecar) |

## Related

  - **[Migrate from custom WebSocket audio](/modalities/voice/migration/from-websocket-audio)** — The WS audio on-ramp that your Twilio envelope shim sends to.
  - **[SIP trunking](/modalities/voice/transport-telephony/sip-trunking)** — Bring the Twilio Elastic SIP trunk — the fully shipped route.
  - **[Codecs & passthrough](/modalities/voice/transport-telephony/codecs)** — Why 8 kHz µ-law lands on a PCMU trunk with zero transcode.
  - **[Migration overview](/modalities/voice/migration/overview)** — Every source we move users off, and the migration checklist.
