You already have a working voice stack — a LiveKit app, a Pipecat bot, a bespoke agent loop, your own ASR/LLM/TTS chain. You do not have to rewrite any of it to run on TeleQuick. This page shows how to keep your runtime and adopt only the transport: real-time audio over QUIC/MoQT on the single :443 plane, without ICE/DTLS/SRTP negotiation or a media server to operate. There are two on-ramps:

LiveKit drop-in shim

Swap one import. Your Room, RoomEvent, and track code keeps compiling and 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 drop-in shim

The compat shim exposes a livekit-client-compatible surface (Room, RoomEvent, createLocalAudioTrack, participants, tracks) mapped onto our MoQT/QUIC transport. For the common voice path — publish the mic, subscribe to remote audio, exchange data messages — 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 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

npm install @telequick/sdk/livekit-compat
2

Swap the import

Point your existing livekit-client imports at the shim. Nothing else in your component changes.
- import { Room, RoomEvent, createLocalAudioTrack } from "livekit-client";
+ import { Room, RoomEvent, createLocalAudioTrack } from "@telequick/sdk/livekit-compat";
3

Connect and go

Your call sites are unchanged — the shim reads the room and identity out of your existing access token.
const room = new Room();
await room.connect(url, token);              // to your relay.telequick.dev endpoint
await room.localParticipant.setMicrophoneEnabled(true);
room.on(RoomEvent.TrackSubscribed, (track) => track.attach());

What maps to what

Under the hood the shim keeps LiveKit semantics but replaces the media plane entirely — there is no SFU, no ICE, and WebRTC’s transport is never used.
LiveKit conceptRuns as
Room.connect(url, token)A MoQT session over WebTransport on :443
Participant identityA room-scoped MoQT namespace (relay enforces isolation via the token)
Microphone trackEncoded Opus frames published as MoQT objects
Remote audio trackA MoQT subscription decoded with WebCodecs into a Web Audio graph
publishData / DataReceivedA reliable MoQT data track
The microphone still goes through the browser’s Opus encoder, but only the encoded frames are extracted and sent — see One-Line WebRTC Diversion for how that codec tap works.
room.connect() refuses to proceed unless the browser supports encoded media transforms (RTCRtpScriptTransform). This is enforced before any audio flows, so 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 exchanging 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: subscribing to a track a peer never published tears down the session, so pass subscribeData: true only when peers are known to publish data. A session-setup race can occasionally deliver zero frames on connect; retry the connect if the first attempt is silent.
The drop-in shim ships for the browser / TypeScript client (the livekit-client surface) today. A server-side Python equivalent (the livekit.rtc surface) is on the roadmap but not yet available — Python agents should use the transport primitives below or point at a realtime endpoint.

Option 2 — use only the transport

If you are not a LiveKit app — you have your own capture/encode/playback, or a non-browser runtime — talk to the transport primitives directly. You keep your runtime; we only carry the frames.
import { MoqtClient, captureMicrophone, OpusPlayer } from "@telequick/sdk";

// 1. Open a MoQT/QUIC session (Chromium-family requires encoded transform).
const client = await MoqtClient.connect(url, token, onState, {
  requireEncodedTransform: true,
});
client.setRoomScope(roomId);   // namespaces must be room-scoped (>= 1 element)

// 2. Publish your mic as Opus objects.
const pub = client.publishAudio(identity, "microphone");
const capture = await captureMicrophone(pub);   // or pass your own MediaStreamTrack

// 3. Subscribe to a peer and play it back.
const player = new OpusPlayer(new AudioContext());
await player.start();
client.subscribeAudio(peerId, "microphone", (tsUs, frame) => player.push(tsUs, frame));

// 4. Side-channel data on a reliable MoQT track.
const data = client.publishFrame(identity, "data");
data.write(BigInt(Date.now()) * 1000n, payload, /* priority */ 0);
Everything on the wire is Opus over MoQT. What sits behind captureMicrophone and OpusPlayer is yours — your VAD, your turn-taking, 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; empty tuples are rejected by the media-over-QUIC engine. 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?

Keeping your runtime and using only the transport is one end of a spectrum. If you would rather hand us the conversation loop too, TeleQuick can run 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 moving 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

How the encoded-frame tap moves an existing WebRTC pipeline onto QUIC.

Agent Runtime Overview

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