:443 plane. There is no ICE/DTLS/SRTP
negotiation and no media server to operate.
There are three on-ramps:
LiveKit Agents plugin
A server-side agent package. Install it into your
livekit-agents
worker. Your entrypoint runs verbatim over our QUIC transport, with no
room.LiveKit drop-in shim
A browser package. Swap one import. Your
Room, RoomEvent, and
track code still compiles, but runs over our transport instead of
LiveKit’s SFU.Raw transport primitives
Wire your own runtime directly to
publishAudio / subscribeAudio. You
own capture, encode, and playback. We move the frames.Option 1 — the LiveKit Agents transport plugin
If your runtime is a server-side LiveKit agent, this is the shortest path. The LiveKit Agents SDK skips its ownRoomIO whenever a session
already has custom audio IO set. The plugin binds that seam to a
QUIC/WebTransport connection to ClutchCall. Your AgentSession, plugins,
provider keys, and tool calls run unchanged. There is no room, no SFU, and no
LiveKit server.
- Python
- TypeScript
cli.run_app) for run_agent_worker. That
runner dials out to :443, registers the worker, and serves engine-assigned
calls. The end-to-end walkthrough is in
Run a LiveKit Agent on ClutchCall Transport.
The limits (pcm16 today, register-and-wait only, outbound UDP required) are
in the integration reference.
Option 2 — the LiveKit drop-in shim (browser)
The compat shim exposes alivekit-client-compatible surface (Room,
RoomEvent, createLocalAudioTrack, participants, tracks). The shim maps
that surface onto our MoQT/QUIC transport. The common voice path
publishes the mic, subscribes to remote audio, and exchanges data messages.
For that path, migration is a one-line import swap.
Preview. The
livekit-compat shim is early-access. It covers the core
voice path (publish/subscribe audio, data messages). But it is not yet a
drop-in for every livekit-client API. Pin a version and test your flows.
If you only need transport, the raw MoQT primitives below are the stable
path.1
Install the compat package
2
Swap the import
Point your existing
livekit-client imports at the shim. Nothing else in
your component changes.3
Connect and go
Your call sites are unchanged. The shim reads the room and identity out
of your existing access token.
What maps to what
Under the hood, the shim keeps LiveKit semantics but replaces the media plane entirely. There is no SFU and no ICE. WebRTC’s transport is never used.
The microphone still goes through the browser’s Opus encoder. But the shim
extracts and sends only the encoded frames. See
One-Line WebRTC Diversion
for how that codec tap works.
Scope of the shim. The voice surface is shipped and proven end-to-end.
Two browsers exchanged live Opus over the transport. Video, screenshare,
and per-participant E2EE key rotation are deferred. The shim is
audio-first. Data-track auto-subscribe is off by default. A subscription to
a track that a peer never published tears down the session. Thus pass
subscribeData: true only when peers are known to publish data. A
session-setup race can occasionally deliver zero frames on connect. If the
first attempt is silent, retry the connect.This shim is browser-only. It re-implements the
livekit-client
surface for a browser app. There is no server-side livekit.rtc
equivalent. A Python or Node agent should use the
transport plugin above.
The plugin covers the server-side case a different way: it replaces the
agent session’s audio IO and does not emulate a room.Option 3 — use only the transport
You may not be a LiveKit app. You may have your own capture, encode, and playback, or a non-browser runtime. In that case, talk to the transport primitives directly. You keep your runtime. We only carry the frames.- TypeScript
- Native runtimes
captureMicrophone and OpusPlayer is yours: your VAD, your turn-taking,
and your model. The transport does not impose a runtime.
Namespace rule. Every track lives under a room-scoped namespace tuple
with a non-empty prefix and suffix. The media-over-QUIC engine rejects
empty tuples.
setRoomScope() (and the shim’s Room) handle this for you.
If you build namespaces by hand, keep them room-scoped, so peers can
discover each other.Runtime on your side, or ours?
You can keep your runtime and use only the transport. That is one end of a spectrum. You can also hand us the conversation loop. Then ClutchCall runs the agent for you, and you bring only the models:From Self-Hosted LiveKit
The full migration path — trunks, tokens, and rollout — for a LiveKit
deployment that moves onto our transport.
BYO Speech-to-Speech
Keep our runtime. Point it at your own realtime speech-to-speech endpoint
with per-tenant credentials.
WebRTC Diversion
Learn how the encoded-frame tap moves an existing WebRTC pipeline onto
QUIC.
Agent Runtime Overview
Learn what our managed runtime does when you want us to drive the
conversation.

