You already have a working voice stack. It can be a LiveKit app, a Pipecat bot, a bespoke agent loop, or your own ASR/LLM/TTS chain. You do not have to rewrite any of it to run on ClutchCall. This page shows how to keep your runtime and adopt only the transport. The transport moves real-time audio over QUIC/MoQT on the single :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 own RoomIO 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.
You then swap your worker runner (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 a livekit-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.
room.connect() refuses to proceed unless the browser supports encoded media transforms (RTCRtpScriptTransform). The shim enforces this before any audio flows. Thus a browser that would silently fall back to plaintext WebRTC fails loudly instead. Today that means Chromium-family browsers. See Browser Compatibility for the fallback ladder.
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.
Everything on the wire is Opus over MoQT. What sits behind 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.