What you’ll build
:443 plane — decodes it to the runtime’s 8 kHz PCM audio bus, and relays
that audio to the bridge over a WebSocket media bridge. The bridge joins your
LiveKit room as an ordinary participant, publishes the caller as a mic track, and
forwards your agent’s audio back the other way.
Shipped edge + a sidecar you run. The gateway-side vendor-bridge node and
WebSocket media handler are in-tree. The bridge sidecar that joins your
LiveKit room is an external component you deploy next to LiveKit with your own
LIVEKIT_URL / API key / secret. The edge is shipped; the bridge is a thin
component you run.Prerequisites
A LiveKit server (cloud or self-hosted) plus a running agent, and its
LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET.API credentials in
TELEQUICK_CREDENTIALS and an inbound SIP trunk or DID (see
SIP Trunking /
Number Provisioning).Go 1.25+ to build the bridge sidecar (a prebuilt
livekit-bridge binary is
also published with the sidecar).Build the mini-app
Have a LiveKit agent + room
Any LiveKit agent works — it needs no TeleQuick awareness. A minimal
LiveKit Agents (Python) worker that a room participant can talk to:Run it as you already do (
agent.py
python agent.py dev). The bridge will drop a
participant into whichever room you name below, and this worker dispatches
onto it — exactly as if a human joined from a browser.Deploy the bridge sidecar
The sidecar is a single Go binary. Build it and run it where it can reach
both the gateway’s WebSocket media bridge and your LiveKit server.It exposes The sidecar is credential-free at rest: it receives the LiveKit URL, key,
secret, room, and participant identity per session in the handshake the
gateway sends — you don’t bake LiveKit secrets into the binary. It’s also
codec-agnostic; the gateway sends LiveKit-appropriate Opus and converts on the
return, so the bridge just forwards frames.
/bridge (the media endpoint the gateway connects to) and
/healthz. Confirm it’s up:Store your LiveKit credentials
Save your LiveKit URL / key / secret as a tenant vendor credential so the
gateway can hand them to the bridge at call time. Reference it by a stable
key (here,
livekit-prod) from the agent config in the next step. Manage
vendor credentials from the console (agent.telequick.dev) or the
control-plane API.Point an agent at the vendor bridge
Instead of a cascade (ASR→LLM→TTS) or a duplex realtime model, the agent’s
entry node is a
With this config the runtime does not run turn detection or a model — it
hands the caller’s audio to the bridge and streams the bridge’s audio back
onto the call. Your LiveKit agent owns the conversation. The gateway-side
bridge endpoint (where the sidecar is reachable) is a deployment-level setting
on the gateway; see Runtime Configuration.
VENDOR_BRIDGE — it routes the call’s media to your
vendor instead of running a model in-engine:agent-config.json
| Field | Meaning |
|---|---|
vendor_provider | livekit | vapi | twilio (swap targets, same mechanism) |
vendor_room | The LiveKit room the bridge joins (your agent dispatches onto it) |
vendor_creds_ref | Key into the tenant vendor credentials from the previous step |
Route a number to the agent
Attach an inbound DID or SIP trunk to this agent so real calls reach it — the
same routing you’d use for any TeleQuick agent. Details in
SIP Trunking and
Number Provisioning.
What happens on the wire
When the call answers, the gateway opens one WebSocket session to the sidecar’s/bridge and sends a JSON handshake naming the vendor plus your LiveKit URL, key,
secret, room, and a participant identity — pulled from the vendor credentials you
stored. Every message after that is one audio frame, in both directions:
- The gateway decodes the caller (G.711 off the PSTN, or Opus off the QUIC leg) to the runtime’s PCM bus, re-encodes to Opus, and streams frames to the bridge.
- The bridge publishes those frames as a LiveKit
MICROPHONEtrack namedcustomer. Your agent subscribes to it like any participant. - The agent’s reply track is subscribed by the bridge, whose Opus payloads flow back over the same WebSocket and onto the call.
vendor_provider. Vapi and Twilio Media Streams ride a
PCM/mulaw WebSocket instead of a LiveKit room, but the loopback shape is identical.
Verify it worked
Everything the gateway gives a native agent, it gives this one — because the edge is unchanged:- Call events & CDR — the call shows up in reports with an
initiated → established → clearedlifecycle and a Q.850 clear cause. - Media quality — per-call MOS / jitter / loss scoring lands on the CDR.
- Recording — both legs are captured if recording is enabled for the trunk.
The
livekit-compat browser shim (a one-import swap that runs a
livekit-client app over MoQT/QUIC with no SFU) is a separate, preview
path — good when your LiveKit surface is a browser app rather than a phone
agent. It’s early-access, Chromium-only, and covers the core audio/data path;
don’t ship it on the critical path yet. Details in
Keep Your Existing Runtime.Related
LiveKit integration reference
Both paths side by side — front-proxy vs. the browser compat shim.
From Self-Hosted LiveKit
The full migration plan — trunks, tokens, rollout.
Keep Your Existing Runtime
The transport-only spectrum for any runtime, not just LiveKit.
Runtime Configuration
Every pipeline node field, including
VENDOR_BRIDGE.