You run a self-hosted LiveKit stack: an SFU that you operate, agents built on the LiveKit runtime, ICE/DTLS/SRTP to negotiate, and a media server to keep alive. This page moves that workload onto ClutchCall without a rewrite of your agent logic. You keep your models, your prompts, and your conversation loop. You drop the SFU and the transport problems. There are three ways in. They solve different problems:

Transport plugin

Recommended. Install a package into your livekit-agents worker. Your agent runs verbatim over our QUIC transport. You retire the SFU completely.

Transport in front

Keep your LiveKit room in operation. Bridge the media into it with a sidecar. Best when other participants still join the room.

Drop-in client shim

Swap one import in your browser app. Your Room / RoomEvent / track code still compiles, but runs over our transport instead of your SFU.
Paths 1 and 2 differ in what you retire. The plugin removes your LiveKit server from the picture entirely. The front-proxy keeps it and puts us in front. You can also give us the conversation loop and bring only your models. For that, see Keep Your Existing Runtime.
The LiveKit Agents SDK skips its own room/WebRTC layer whenever a session already has custom audio IO set. This is the same seam that LiveKit uses for its TCP console mode. The transport plugin binds that seam to a QUIC/WebTransport connection to ClutchCall. Thus your entrypoint runs byte-for-byte unchanged. There is no room, no SFU, and no LiveKit server anywhere in the path.
1

Install the plugin next to your agent

2

Swap your worker runner

Replace cli.run_app(...) with run_agent_worker(...). Pass your existing entrypoint. The worker dials out to :443, registers under an agent name, and serves the calls that the engine assigns. It reconnects automatically if the link drops. This is the only new file in your repo. The agent itself does not change.The full worker snippet for both languages is in Run a LiveKit Agent on ClutchCall Transport.
3

Point your inbound numbers at us

Move the trunks that feed your LiveKit agent onto ClutchCall. Then attach an agent whose entry node is a VENDOR_BRIDGE with vendor_provider: "clutchcall" and vendor_room set to the name that your worker registered under.See SIP Trunking and Number Provisioning.
4

Cut over and retire the SFU

Move numbers in waves. When traffic is stable, your LiveKit server carries no voice traffic at all. Shut it down, along with the TURN relays and the ICE plumbing.
Check two things before you commit to this path. The worker needs outbound UDP/443 for media (the WebSocket fallback is presence-only). The worker also runs in register-and-wait mode: a long-lived process, not a scale-to-zero serverless function. If either is a problem, take Path 2.

Path 2 — put the transport in front of your agent

Take this path when your LiveKit room must stay in operation — other participants join it, or something else in your stack depends on it. Your LiveKit agent, room logic, and model plumbing stay byte-for-byte the same. What changes is the media plane in front of them. This path also covers the two cases that the plugin does not yet: a scale-to-zero serverless worker, and a network with no outbound UDP. Callers (PSTN via SIP trunks, or browsers) land on ClutchCall’s single :443 QUIC/WebTransport plane. A lightweight bridge sidecar that you run next to your LiveKit deployment carries the audio between our transport and your LiveKit room. The runtime routes a call to that bridge with a first-class vendor-bridge step. The runtime does not drive the conversation itself.
The vendor-bridge routing is a first-class part of the runtime. Its targets are livekit, vapi, and twilio. The bridge sidecar itself is a separate component that you deploy next to your LiveKit stack. It is not bundled into the engine. Its exact packaging depends on your environment. Unlike Path 1, this path keeps your LiveKit server in the call flow.
1

Point your inbound numbers at our transport

Move the trunks that feed your LiveKit agent onto ClutchCall. Your carrier trunk terminates on our SIP gateway. Numbers are provisioned per tenant under <workspace-id>.sip.clutchcall.dev. Nothing about your agent changes. Only the place where the call first lands changes.See SIP Trunking and Number Provisioning.
2

Run the LiveKit bridge sidecar

Deploy the bridge sidecar next to your LiveKit server. It connects to our transport on one side and to your LiveKit room on the other. It uses your existing LiveKit URL and access-token issuance. Audio moves across as Opus. Your agent sees a normal LiveKit participant.
3

Route calls to the bridge

In the agent configuration for the number, select the vendor bridge target instead of the built-in runtime. As a concept, the agent config carries a vendor-bridge block that names LiveKit as the destination:
When a call matches, the runtime gives its media to the bridge. It does not run a conversation DAG. Your LiveKit agent joins the room and answers as it always did.
4

Cut over and validate

Move numbers over in waves. On each wave, check that the call reaches your LiveKit agent. Check that audio is two-way. Check that your existing telemetry still fires. The agent is unchanged, so the only new surface is the transport and the bridge. Validate those, not your model.
This path is a step, not a dead end. When traffic is stable on our transport, you can move to Path 1: install the plugin, then drop the sidecar and the room. Or you can migrate the runtime itself. Swap the vendor bridge for our managed runtime with your own models (BYO ASR/LLM/TTS or BYO Speech-to-Speech). Then retire the LiveKit stack when you are ready.

Path 3 — the drop-in client shim (Preview)

Preview / early access. The livekit-compat shim covers the core browser voice path: publish the mic, subscribe to remote audio, exchange data messages. But it is not yet a drop-in for every livekit-client API. Video, screenshare, and per-participant E2EE key rotation are deferred. A session-setup race can occasionally deliver zero frames on the first connect (retry). Pin a version. Test your flows. For a stable transport-only path, use the raw MoQT primitives in Keep Your Existing Runtime.
Your LiveKit usage can be a browser app that talks to a room. For that case, the shim exposes a livekit-client-compatible surface (Room, RoomEvent, createLocalAudioTrack, participants, tracks) mapped onto our MoQT/QUIC transport. For the common voice path, the migration is a one-line import swap. There is no SFU, no ICE, and no media server.
1

Install the shim

2

Swap the import

Point your existing livekit-client imports at the shim. Your component code does not change in any other way.
3

Connect to the relay

Your call sites stay the same. The shim reads room and identity from your access token. It opens a MoQT session on :443 instead of a join to an SFU.

How the shim maps LiveKit concepts

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 mic still runs through the browser’s Opus encoder. But the shim taps only the encoded frames and sends them over MoQT. See One-Line WebRTC Diversion.
room.connect() refuses to proceed unless the browser supports encoded media transforms (RTCRtpScriptTransform). It fails loudly. It does not fall back silently to plaintext WebRTC. Today that means Chromium-family browsers. Also, the browser client is WebTransport-only (no WebSocket fallback leg yet). See Browser Compatibility.

What you gain, and what stays the same

You drop the SFU that you operate, the ICE/DTLS/SRTP negotiation, the TURN relays for blocked networks, and the media server’s scale and on-call burden. Media moves on one QUIC/WebTransport plane on :443.
You keep your agent logic, prompts, and models. On Path 1, your entrypoint and AgentSession run verbatim. On Path 2, your LiveKit room code is unchanged. On Path 3, your browser component keeps its Room/RoomEvent shape. We move frames. We do not impose a runtime.
Paths 2 and 3 read the room and identity out of your existing LiveKit access token. Thus your auth/token-minting service does not have to change to get audio to flow. Path 1 has no room and no LiveKit token. The worker authenticates to the edge with a ClutchCall media app key + secret (CLUTCHCALL_MEDIA_KEY / CLUTCHCALL_MEDIA_SECRET). Your provider keys stay where they are, in your own process.

Run a LiveKit Agent on the Transport

Path 1 end to end — install the plugin, register a worker, take a call.

Keep Your Existing Runtime

The transport-only on-ramps in depth — the shim and the raw MoQT primitives.

LiveKit Agent Integration

Run a LiveKit agent behind our transport, at the integration level.

Migration Overview

Choose a path across all the products that you can move off.

Migration Checklist

The wave-by-wave cutover checklist for any voice migration.