AudioBridge helper in the SDK is a thin
wrapper — under it, every call’s audio is just two live MoQT tracks fanned out by
the relay. This page is the drop-below-the-SDK surface: the raw
publish_audio / subscribe_audio primitive, the exact namespace layout for a
call, the capability tags that route a track, and how the relay authorizes you to
touch a namespace. Reach for it when you’re bridging your own runtime, taping a
call into a recorder, or wiring a human agent to a live caller.
Read Realtime Tracks (MoQT) first for the general
model (namespace + name + capability, connect/backoff/reconnect, track kinds).
Everything here is that model, pinned to voice.
The two tracks of a call
Every call is keyed by itscall_sid (see
Sessions, calls, tracks & streams).
Its audio lives under a per-call namespace, split by direction, with the track
name audio:
| Namespace | Direction | Carries |
|---|---|---|
voice/<sid>/uplink | caller → cloud | what the caller is saying |
voice/<sid>/downlink | cloud → caller | what gets played back to the caller |
uplink and subscribes
downlink for playback, so which track you publish vs. subscribe depends on
which side you are:
- Agent / server side (the common case) — subscribe
uplinkto hear the caller, publishdownlinkto talk back. - Browser-caller side — the mirror: subscribe
downlinkto play cloud audio, publishuplinkwith captured mic frames.
Uplink and downlink are distinct namespaces on purpose — a single track
would feed the agent its own output. The same split lets a human agent or a
supervisor subscribe
uplink alone and hear only the caller (see
Handoffs).Publish & subscribe
publish_audio(ns, name, …) returns a publication whose write(ts_us, frame)
sends one encoded audio frame (e.g. a 20 ms Opus packet); objects roll into a new
group about once a second. subscribe_audio(ns, name, on_frame) delivers each
object back as (ts_us, frame). Hold both handles for the life of the call —
dropping a subscription frees the native callback, and dropping a publication
stops the track.
- TypeScript
- Python
Per-call track namespace,
voice/<sid>/uplink or voice/<sid>/downlink. <sid>
is the call_sid.Track name within the namespace —
audio for voice legs.Free-form routing intent (see Capability tags). Voice legs use
voice/<codec>, e.g. voice/opus.Codec sample rate of the frames you write. Opus stays
48000; narrowband PCM/G.711
legs use 8000.Channel count. Voice is mono.
Frame duration per object. 20 ms is the wire cadence for Opus and G.711.
Raw objects vs. decoded frames
The RTP media plane publishes the caller’s audio onuplink as raw
8 kHz PCM16 LE objects — the runtime’s internal audio format — with no SDK frame
wrapper. To consume those bytes verbatim, use subscribe_raw, which hands you each
MoQT object’s payload untouched:
subscribe_audio when the other side is an SDK publisher (frames carry a
timestamp header); use subscribe_raw for engine-published raw tracks. See the
audio contract for the
8 kHz PCM16 / Opus / G.711 details.
Capability tags
A capability is an open-ended intent string attached to a track. The relay and in-engine modules route on it — “route on intent, not on media kind.” Voice legs tag themselvesvoice/<codec>, where <codec> is one of the SDK’s audio codecs:
| Codec value | On the wire |
|---|---|
opus | Opus 48 kHz mono, 20 ms |
pcm16 | linear PCM16 |
g711_ulaw | G.711 µ-law (PT 0) |
g711_alaw | G.711 A-law (PT 8) |
voice/opus, a passthrough leg off a µ-law trunk
publishes voice/g711_ulaw. Capability is informational for the track itself —
subscribers select which namespace/name to receive; the tag tells the routing layer
what the bytes are so it can hand them to the module that registered for that
intent (an ASR node, a recorder, a media.passthrough relay).
Namespace authorization
There is no separate ACL call. Authorization rides the connect token:Present your tenant token at connect
Pass your workspace API key as the token to
connect. The SDK sends it as a URL
query parameter to relay.telequick.dev, where the relay control plane verifies it
before the MoQT session opens.The relay scopes you to your workspace's calls
A verified session may publish and subscribe
voice/<sid> namespaces for calls that
belong to your workspace. <sid> values are unguessable per-call keys, and the
relay rejects a namespace outside your tenant scope — cross-tenant subscription is
not possible.Namespaces are non-empty tuples
MoQT namespaces are tuples that must have at least one element; a subscribe for a
namespace that hasn’t been announced yet is held by the relay and delivered once
the publisher appears. For multi-participant rooms the SDK scopes each participant
under a
[room, identity] tuple; a single call’s audio lives under the flat
voice/<sid>/… namespace.Transport & runtimes
Tracks ride MoQT over WebTransport on the single QUIC/HTTP-3:443 plane.
Browsers negotiate the moqt-16 WebTransport subprotocol; the relay handles
connect, capped-backoff reconnect, and re-establishing every publication and
subscription for you.
Two runtimes sit behind one API. The native SDK cores (Python, Go, Rust, and
the C++ FFI, telequick_moqt_ffi) speak QUIC and carry a QUIC → WebSocket
fallback ladder for UDP-blocked networks. The TypeScript SDK is a standalone
WebTransport implementation and is WebTransport-only today — there is no browser
WS fallback yet. Hold the two as separate conformance surfaces; the publish/subscribe
APIs match, but queueing and unsubscribe lifecycle can differ.
For browser callers you rarely call this primitive directly — the SDK’s
WebRTC-diversion capture wraps
publish_audio with encoded-Opus tapping. See
Browser audio capture and
WebTransport.Related
Realtime Tracks (MoQT)
The general publish/subscribe model this page pins to voice.
Sessions, calls, tracks & streams
How call_sid keys the SIP dialog, media session, and every track.
AudioBridge SDK
The high-level helper that wraps these tracks per call.
Handoffs
Subscribe voice/<sid>/uplink to put a human on a live caller.