Once you own the audio bridge for a call, everything your text-to-speech engine produces goes to the caller through one method: publishDownlink(frame). Each call pushes a single encoded audio frame — typically a 20 ms Opus packet — onto the voice/<sid>/downlink track, and the engine paces it to the caller’s leg (transcoding to G.711 on a PSTN call for you). This is the send side of the bring-your-own ASR/TTS pattern. For the receive side — feeding caller speech into your recognizer — see Stream Audio to ASR.

Push TTS chunks as they arrive

Open the bridge with attach(), then forward each synthesized chunk straight to publishDownlink. attach() always subscribes uplink too, so pass an onUplink callback even if you only care about playback here (a no-op is fine, or wire it to your ASR).
import { Voice } from "@telequick/sdk/voice";

const v = new Voice({
  baseUrl: "https://engine.telequick.dev",
  apiKey:  process.env.TELEQUICK_CREDENTIALS!,
  orgId:   "org_abc",
});

const call = await v.calls.originate({
  to: "+15551234567",
  from: "+15558675309",
  trunkId: "trunk_main",
});

const bridge = await v.audioBridge.attach(call.sid, {
  codec: "opus",
  onUplink: (frame, tsUs) => {}, // playback-only here
});

// stream synthesized Opus straight to the caller
myTts.onChunk((opus) => bridge.publishDownlink(opus));
publishDownlink expects an encoded frame in the codec you asked for in attach(). With codec: "opus" push Opus packets; with codec: "pcm16" push raw PCM16. Send one frame per call — a 20 ms tick — rather than a whole utterance in a single buffer, so playback stays smooth and barge-in can cut it off cleanly.

Feed a model that emits raw PCM

Many realtime and TTS models hand you 16-bit PCM rather than Opus. Ask for pcm16 and the bridge transcodes both directions — you push exactly what the model gives you.
const bridge = await v.audioBridge.attach(call.sid, {
  codec: "pcm16",
  sampleRate: 16000,
  onUplink: (pcm, tsUs) => model.appendAudio(pcm),
});

// model → caller: no re-encode in your process
model.onAudioOut((pcm) => bridge.publishDownlink(pcm));

Time playback yourself

publishDownlink stamps each frame with a monotonic clock when you omit the timestamp — fine for live streaming. If you are replaying buffered audio or splicing clips, pass a microsecond timestamp so frames land in order.
let tsUs = 0n;
for (const frame of opusFrames) {
  bridge.publishDownlink(frame, tsUs);
  tsUs += 20_000n; // 20 ms per frame, in microseconds
}
Keep the bridge reference alive for the whole call — if it is garbage-collected both tracks drop. When the call ends, close() the bridge before you hangup() so the downlink track tears down cleanly.

Stream Audio to ASR

The uplink companion — feed caller speech to your recognizer.

BYO ASR / LLM / TTS

Run the full custom-brain loop end to end.

Hand off to a human

Escalate the live call while keeping the same sid.

Python SDK

Full AudioBridge signatures for Python.