You have a Pipecat bot — a pipeline of frame processors wired to ASR, an LLM, and TTS — and you want it to answer real phone calls and browser sessions over TeleQuick’s transport, without operating a media server or negotiating ICE/DTLS/SRTP. This recipe assembles that end to end: a small host process that takes a live call, subscribes the caller’s audio, runs it through a Pipecat pipeline, and streams the agent’s replies back — all over QUIC/MoQT on the single :443 plane.
No packaged Pipecat adapter ships. There is no pipecat transport, plugin, or vendor-bridge target in TeleQuick today — the built-in VENDOR_BRIDGE routes only to LiveKit, Vapi, and Twilio backends. What is real and shipping is the audio bridge this recipe is built around: caller audio in, agent audio out, over raw MoQT. You own the thin glue that hands those frames to Pipecat. The QuicMoqtTransport wrapper near the end is an illustrative design sketch of a future first-class adapter — do not pip install it. For the conceptual framing, see Pipecat + TeleQuick Transport.

What you’re building

Pipecat and TeleQuick sit at different layers, which is why they compose cleanly: Pipecat owns the conversation (the frame pipeline, turn-taking, and your model choices), and TeleQuick owns the transport and telephony (the SIP gateway/B2BUA that terminates the carrier trunk, and the media plane that carries the audio over QUIC). The seam between them is a pair of tracks keyed by the call id — an uplink (caller → bot) and a downlink (bot → caller):
  caller ──SIP/RTP──▶ SIP gateway / B2BUA ──▶ QUIC/MoQT media plane

                             voice/<call_sid>/uplink │ (caller audio, PCM16)

   ┌─────────────────────── your host process ───────────────────────┐
   │  AudioBridge.onUplink ─▶ Pipecat pipeline ─▶ AudioBridge.publish │
   │     (SDK, real)          (STT → LLM → TTS)     Downlink (SDK)     │
   └──────────────────────────────────────────────────────────────────┘

                           voice/<call_sid>/downlink │ (agent audio, PCM16)
The two AudioBridge calls are stock SDK. Everything between them is your Pipecat pipeline — unchanged from any other Pipecat deployment.

Build the app

1

Take the call and attach the audio bridge

Skip agents.attach (that runs our managed runtime) and open the bridge yourself. Ask for pcm16 at 16 kHz — the shape most frame pipelines expect; the bridge transcodes the PSTN (G.711) leg and the runtime’s 8 kHz bus for you in both directions.
import { Voice } from "@telequick/sdk/voice";

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

// Originate (or attach to an inbound sid you already hold).
const call = await v.calls.originate({
  to: "+15551234567",
  from: "+15558675309",
  trunkId: "trunk_main",
});

// pcm16 @ 16 kHz — raw audio in both directions.
const bridge = await v.audioBridge.attach(call.sid, {
  codec: "pcm16",
  sampleRate: 16000,
  onUplink: (pcm, tsUs) => pipeline.pushAudio(pcm),      // caller -> bot
});
2

Wrap the two hooks as a Pipecat transport

Pipecat’s Transport seam needs exactly two things: something that produces input audio frames and something that consumes output audio frames. The bridge already gives you both — onUplink is the producer, publishDownlink is the consumer. Wrap them so the rest of the pipeline runs unchanged.
Illustrative — you own this class today. The transport below is your glue code around the real audio bridge, not an installable package. Class and frame names track Pipecat’s public shape; pin your Pipecat version and match its exact transport base classes when you write it for real.
# ILLUSTRATIVE glue you write today — wraps the REAL audio bridge for Pipecat.
from pipecat.frames.frames import InputAudioRawFrame, OutputAudioRawFrame
from pipecat.processors.frame_processor import FrameProcessor

class MoqtBridgeInput(FrameProcessor):
    """Emits caller uplink audio as Pipecat input frames."""
    def __init__(self, sample_rate: int = 16000):
        super().__init__()
        self.sample_rate = sample_rate

    def on_uplink(self, pcm: bytes, ts_us: int):
        frame = InputAudioRawFrame(audio=pcm, sample_rate=self.sample_rate, num_channels=1)
        # hand the frame downstream into the pipeline
        self.push_frame(frame)

class MoqtBridgeOutput(FrameProcessor):
    """Publishes agent output frames back as downlink audio."""
    def __init__(self, bridge):
        super().__init__()
        self.bridge = bridge

    async def process_frame(self, frame, direction):
        await super().process_frame(frame, direction)
        if isinstance(frame, OutputAudioRawFrame):
            self.bridge.publish_downlink(frame.audio)   # bot -> caller (SDK, real)
        await self.push_frame(frame, direction)
3

Assemble the pipeline

Drop the two wrappers on the ends of an ordinary Pipecat pipeline. The STT, LLM, and TTS processors are stock Pipecat — pick whatever providers you already use.
# ILLUSTRATIVE — a standard Pipecat pipeline between the two bridge wrappers.
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineTask

bridge_in  = MoqtBridgeInput(sample_rate=16000)
bridge_out = MoqtBridgeOutput(bridge)

# route the SDK's uplink callback into the input processor
# (v.audio_bridge.attach(..., on_uplink=bridge_in.on_uplink))

pipeline = Pipeline([
    bridge_in,     # caller audio in
    stt,           # your ASR frame processor
    llm,           # your LLM context
    tts,           # your TTS frame processor
    bridge_out,    # agent audio back to the caller
])
4

Run the call, then tear down cleanly

Run the pipeline for the life of the call. When the conversation ends, close the bridge before hanging up so both tracks tear down cleanly.
// … your pipeline runs; when it decides the call is over:
await bridge.close();     // release both tracks first
await call.hangup();
Keep the bridge reference alive for the whole call. If it is garbage-collected mid-call, both tracks drop and the audio goes silent.

Where this is headed

A first-class integration would collapse the two wrapper classes above into a single Pipecat Transport that binds directly to our media-over-QUIC primitives — you’d construct it with the call id and drop transport.input() / transport.output() onto the pipeline, with no onUplink/publishDownlink glue to write.
Design sketch — not shipped. QuicMoqtTransport does not exist as a package. It shows the intended shape of a future adapter only.
# ILLUSTRATIVE — this transport is not shipped. Design sketch only.
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineTask

# A transport that would carry Pipecat frames over the QUIC/MoQT plane:
#   input():  subscribe voice/<call_sid>/uplink   -> InputAudioRawFrame
#   output(): publish   voice/<call_sid>/downlink  <- OutputAudioRawFrame
transport = QuicMoqtTransport(
    url="quic://relay.telequick.dev",
    call_sid=call.sid,
    sample_rate=16000,
)

pipeline = Pipeline([
    transport.input(),   # caller audio in
    stt, llm, tts,       # your pipeline, unchanged
    transport.output(),  # agent audio back to the caller
])

await PipelineRunner().run(PipelineTask(pipeline))
The only real work such an adapter adds is the seam our own runtime already handles: resample between Pipecat’s frame rate and the runtime’s 8 kHz PCM16 audio bus, keep the room-scoped MoQT namespaces valid, and carry Opus rather than PCM on browser legs. None of it is exotic — it just isn’t packaged yet, so today you own the wrapper.

Other on-ramps that ship today

If you don’t want to write even the wrapper, two seams already let a Pipecat bot take live calls:
Pipecat already speaks WebSocket audio, and TeleQuick accepts a bespoke WebSocket audio sender as a media leg. Point Pipecat’s WebSocket server transport at that on-ramp and route the trunk’s inbound rule to your endpoint — the SIP gateway terminates the carrier while Pipecat drives the conversation, with no TeleQuick code in the bot. Same path as migrating a custom WebSocket audio sender.
If you want Pipecat only for the pipeline — cascaded ASR → LLM → TTS, turn-taking, tools — TeleQuick’s runtime already ships that as a configured DAG with transport, telephony, and barge-in built in. Bringing only your models via provider credentials is often less work than porting a Pipecat pipeline onto an adapter that doesn’t exist yet.

Pipecat + Transport

The full conceptual framing and the on-ramps that ship today.

Bridge a Pipecat Agent

The single-snippet version of the audio-bridge seam used above.

LiveKit Agent with TeleQuick Transport

The sibling recipe for a LiveKit agent over the same transport.

Keep Your Existing Runtime

Adopt only the transport and keep your own agent loop.