You already have a runtime that works — a bespoke agent loop, a Pipecat bot, a LiveKit app, your own ASR/LLM/TTS chain wired to logic you trust. You do not have to rebuild it to run on TeleQuick. Keep your runtime and adopt only the transport: a call’s audio, delivered to your code and carried back, over QUIC/MoQT on the single :443 plane — no ICE/DTLS/SRTP negotiation and no media server to operate. There are three ways to plug a runtime in, from least code to most control:

Vendor bridge

A config node, no code. Point a call at an external orchestration vendor (LiveKit / Vapi / Twilio); we stay the transport in front of it.

Raw MoQT bridge

Subscribe the caller’s uplink, publish your reply as downlink. You own capture, model, and playback; we move the frames. The stable path.

LiveKit-compat shim

A livekit-client-shaped surface over our transport. Swap one import.

Route 1 — bridge to an external vendor

If your “runtime” is really another orchestration product — a LiveKit Agent, a Vapi assistant, a Twilio app — you do not integrate it in code at all. You add a VENDOR_BRIDGE node to the agent’s pipeline config. When the runtime sees it, it hands the call’s media to that vendor over an external bridge sidecar instead of running our own ASR→LLM→TTS pipeline; TeleQuick stays the QUIC transport in front of the vendor’s media backend.
This is a media bridge, not a packaged adapter. There is no in-tree LiveKit-Agents or Pipecat adapter — the vendor bridge routes audio to an external sidecar you run alongside your vendor account, and that sidecar ships separately from the engine. It is a partial, self-operated integration: it moves audio to and from the vendor; it does not import the vendor’s SDK for you.
The config is a single node in the agent’s pipeline document:
{
  "entry_node": "BRIDGE",
  "pipeline": {
    "BRIDGE": {
      "node_type": "VENDOR_BRIDGE",
      "vendor_provider": "livekit",          // "livekit" | "vapi" | "twilio"
      "vendor_room": "support-room-42",       // LiveKit room / Vapi assistant id / Twilio app
      "vendor_creds_ref": "livekit-primary"   // key into this tenant's sealed vendor credentials
    }
  }
}
vendor_provider
string
required
The external orchestration vendor: livekit, vapi, or twilio. Selects which bridge protocol the sidecar speaks.
vendor_room
string
The vendor-side target — a LiveKit room, a Vapi assistant id, or a Twilio app.
vendor_creds_ref
string
A reference into the tenant’s stored vendor credentials (sealed at rest), so the vendor’s keys never live in the pipeline document.
Edit this in the console at agent.telequick.dev or through the control-plane API, then point a trunk or an outbound call at the agent. See the vendor-specific pages for the exact room/credential shape:

LiveKit Agent

Run a LiveKit agent behind our transport.

Pipecat

Run a Pipecat pipeline behind our transport.

Route 2 — the raw MoQT audio bridge

This is the direct path and the one most custom runtimes want: attach to a live call, receive the caller’s audio frames, and push your reply frames back. Every call is addressed by its call_sid, and its audio lives on two MoQT tracks under a room-scoped namespace:
TrackDirectionYou
voice/<sid>/uplinkcaller → yousubscribe to hear the caller
voice/<sid>/downlinkyou → callerpublish your runtime’s reply
The SDK’s AudioBridge wraps both tracks behind one handle: attach() subscribes the uplink and publishes the downlink for you. What sits between onUplink and publishDownlink is entirely yours — your VAD, your turn-taking, your model.
1

Attach to the call

Open the bridge with the call’s call_sid. Choose the codec that matches the leg — opus for browser/app callers, g711_ulaw/g711_alaw or pcm16 for phone callers.
2

Consume the uplink

Your onUplink callback fires for every inbound audio frame. Feed it to your runtime’s input — your ASR, your speech-to-speech session, whatever you run.
3

Publish the downlink

When your runtime produces reply audio, call publishDownlink(frame) per encoded frame (for example one 20 ms Opus packet). The caller hears it.
4

Close on teardown

Release the handle when the call ends — it stops both tracks and ends the bridge.
import { Voice } from "@telequick/sdk/voice";

const voice = new Voice({
  baseUrl:   "https://engine.telequick.dev",
  apiKey:    process.env.TELEQUICK_API_KEY!,
  orgId:     "org_xxx",
  relayHost: "relay.telequick.dev",
});

// Attach to a live call by its call_sid: uplink is subscribed,
// downlink is published — both as MoQT tracks under voice/<sid>.
const bridge = await voice.audioBridge.attach(callSid, {
  codec: "opus",                        // opus | pcm16 | g711_ulaw | g711_alaw
  onUplink: (frame, tsUs) => {
    myRuntime.pushCallerAudio(frame, tsUs);   // feed YOUR model
  },
});

// When your runtime produces reply audio, play it back to the caller.
myRuntime.onReplyAudio((frame) => bridge.publishDownlink(frame));

// ...on hangup
await bridge.close();
Same shape in every SDK. Go, Rust, Java, and .NET expose the same attach / publish_downlink primitives through a shared native core. The native bindings speak QUIC/MoQT directly and do not ship the browser’s WebSocket/WebRTC fallback leg — plan for a QUIC-reachable network. See the transport SDK reference for per-language method and event names.
Namespace rule. Track namespaces are room-scoped tuples with a non-empty prefix and suffix; empty tuples are rejected by the media-over-QUIC engine. The voice/<sid>/{uplink,downlink} convention satisfies this, and the SDK builds the namespaces for you from the call_sid. If you hand-roll namespaces, keep them room-scoped. More on the object model in Sessions, Calls, Tracks & Streams.

Where the call_sid comes from

You attach a bridge to a call you already have. For an outbound call you get the call_sid back from voice.calls.originate. For inbound calls, drive the leg to your bridge instead of the built-in runtime by pointing the trunk’s inbound rule at your integration, then attach as each call lands. The telephony transport section covers trunk routing.

Route 3 — the LiveKit-compatible shim

If your runtime is a browser LiveKit app, the livekit-compat shim exposes a livekit-client-shaped surface (Room, RoomEvent, tracks, participants) mapped onto our MoQT/QUIC transport. For the core voice path — publish the mic, subscribe to remote audio, exchange data messages — migration is a one-line import swap, with no SFU and no ICE.
Preview. The livekit-compat shim is experimental and audio-first — it covers the core voice path but is not yet a drop-in for every livekit-client API, and video/screenshare are deferred. It ships for the browser / TypeScript client today; there is no server-side Python equivalent yet. Pin a version and test your flows. The full walkthrough, including the RTCRtpScriptTransform requirement and what maps to what, lives in Keep Your Existing Agent Runtime.

Which route?

Vendor bridge

Your runtime is LiveKit/Vapi/Twilio orchestration. Config-only; you run the external sidecar.

Raw MoQT bridge

Custom or non-browser runtime, or a server-side loop. Most control, fully shipped, every SDK.

LiveKit-compat shim

A browser LiveKit app you want to move with minimal edits. Preview.

Keep Your Existing Runtime

The full browser story: the shim, the transport primitives, and the codec tap that moves an existing WebRTC pipeline onto QUIC.

BYO Speech-to-Speech

Keep our runtime and point it at your own realtime endpoint with per-tenant credentials.

Agent Runtime Overview

What the managed runtime does when you want us to drive the conversation.

From Self-Hosted LiveKit

The end-to-end migration path for a LiveKit deployment moving onto our transport.