Audio I/O: How Calls Enter Your System

Lesson 2 · ~12 min · August 2026

Lesson 1 said every voice agent has five layers, and Audio I/O is the first and last. This lesson unpacks that layer. By the end, you'll understand exactly what happens between the moment a caller dials a number and the moment raw audio packets hit your STT engine — and the reverse path for TTS output.

The Big Picture

There are two worlds: the public switched telephone network (PSTN) — copper wires, cell towers, 100-year-old infrastructure — and the IP network where your voice agent lives. Bridging them is the Audio I/O layer's entire job.

PSTN WORLD
  Caller's phone → Cell tower → Carrier network
▼ SIP trunk (Twilio/Telnyx)
IP WORLD
  SIP signaling (call setup) → RTP media (audio packets) → Your voice agent
  OR
  Browser WebRTC → LiveKit SFU → Your voice agent

Two paths into the IP world: SIP (phone calls) and WebRTC (browser/app calls). They solve the same problem — getting real-time audio into your server — with different tradeoffs. Let's take them one at a time.

Path 1: SIP — The Phone Call Path

What SIP Actually Does

SIP (Session Initiation Protocol) is signaling only. It handles the lifecycle of a call — setup, modification, teardown — but never touches the audio itself. Think of it as the handshake before the conversation.

SIP SIGNALING (TCP/UDP port 5060)
Caller dials → INVITE → 100 Trying → 180 Ringing → 200 OK → ACK → [call active]
RTP MEDIA (UDP, dynamic ports)
Audio packets stream directly between endpoints, bypassing SIP entirely

The key insight: SIP and RTP are separate. SIP runs on port 5060 and negotiates the call. Once both sides agree, RTP (Real-time Transport Protocol) opens a direct UDP stream for the audio. The SIP provider (Twilio, Telnyx) acts as a gateway — it speaks SIP to the phone carrier and RTP to your server.

SIP Trunking In Practice

A SIP trunk is a virtual phone line. Instead of a physical copper wire, you get an IP endpoint that can handle multiple simultaneous calls. Twilio gives you a phone number, routes incoming calls to your SIP server, and sends outgoing calls through their carrier network.

Cost context: A Twilio Colombian number is $14/month. Inbound calls cost $0.0945/min. Telnyx is 50-70% cheaper at scale but has a steeper setup. For an MVP, start with Twilio.

LiveKit's SIP Bridge

You don't handle raw SIP yourself. LiveKit Agents includes a built-in SIP bridge: it speaks SIP on port 5060, terminates the media, and converts the call into a room — the same abstraction it uses for WebRTC. From your agent's perspective, a phone call and a browser call look identical.

# livekit.yaml — enable SIP
sip:
  enabled: true
  listen_address: "0.0.0.0:5060"

# Each incoming call becomes a room
# Your agent joins it like any other room
await ctx.connect()  # same whether call came via SIP or WebRTC

The dispatch rule is what routes specific phone numbers to specific agents — you set this once per trunk and LiveKit handles the rest.

Path 2: WebRTC — The Browser Path

WebRTC (Web Real-Time Communication) is the browser-native way to send audio and video peer-to-peer. It's what powers Google Meet, Discord, and every in-browser calling app.

How WebRTC Works

Unlike SIP, WebRTC bundles signaling and media into one stack. The browser uses:

The browser needs help establishing a direct connection. ICE (Interactive Connectivity Establishment) tries multiple paths: direct connection first, then STUN (a server that tells each peer its public IP), then TURN (a relay server when firewalls block everything).

Why LiveKit Exists

Direct peer-to-peer WebRTC doesn't scale. You need a server in the middle — a Selective Forwarding Unit (SFU) — that receives every stream and routes them intelligently. LiveKit is that SFU. Your voice agent connects to LiveKit as just another participant in the room, receiving audio from the caller and sending TTS audio back.

WebRTC vs SIP for voice agents: WebRTC is simpler for prototypes — open a browser tab, grant mic access, done. SIP is mandatory for real phone calls. In production, you'll likely need both: SIP for phone callers, WebRTC for embedded browser widgets and testing.

The Codec Pipeline: Why Phone Audio Is Different

This is the most practically important section of this lesson. It explains why you can't just use any STT engine for phone calls.

CodecSample RateBitrateUsed InSTT Impact
G.711 μ-law8 kHz64 kbpsPSTN (landlines, old cell calls)⚠️ Narrowband — 2-3× higher WER with Whisper
G.711 a-law8 kHz64 kbpsPSTN (Europe/LatAm)Same as μ-law
G.72216 kHz48-64 kbpsHD Voice (modern VoIP)Decent — wideband, 2× the frequency range
Opus8-48 kHz6-510 kbpsWebRTC, modern VoIP✅ Best — full-band option, adaptive

The critical number: 8 kHz sample rate = 4 kHz maximum frequency. Human speech extends to ~8 kHz, but phone calls literally chop off the top half. The "s" and "f" sounds (sibilants) live in that 4-8 kHz range, which is why phone calls sometimes make it hard to distinguish "six" from "fix."

This is why local Whisper fails on phone calls. Whisper models are trained on 16 kHz wideband audio. When you feed them 8 kHz narrowband phone audio, the model hears muffled, missing frequencies it was trained to expect. Word error rate (WER) jumps 2-3×. Colombian accents compound this — the model is already working harder on the accent and now it's missing half the frequency spectrum. Use Deepgram Nova-3 for production phone STT. It's trained on varied sample rates and handles narrowband natively.

The Complete Audio I/O Flow

Let's trace a real phone call through every Audio I/O step:

1. Caller dials +57 312 345 6789
   ↓ PSTN signaling (SS7, carrier network)
2. Twilio receives the call at their Colombian POP
   ↓ SIP INVITE sent to your LiveKit server (port 5060)
3. LiveKit SIP bridge: 200 OK → ACK (call established)
   ↓ Codec negotiation: G.711 a-law selected (64 kbps)
4. RTP audio packets start flowing
   ↓ 20ms frames, 160 bytes each, UDP
5. LiveKit internal: audio decoded, resampled if needed
   ↓ Audio frames delivered to your agent session
6. STT engine receives clean audio frames
   ↓ Transcription starts (interim results flow back)
7. LLM generates response → TTS synthesizes audio
   ↓ Audio frames stream back through LiveKit
8. LiveKit encodes → RTP → Twilio → PSTN → caller hears response

How to Choose: Your Decision Framework

If you need…Use…Because…
Real phone calls (any phone, any network)SIP trunk (Twilio) + LiveKitOnly path into the PSTN. Non-negotiable for production.
Browser-based calls (web app widget)WebRTC + LiveKitZero phone cost, full-band Opus audio, simple setup.
Testing during developmentWebRTC (browser)Open localhost, grant mic, done. No phone numbers needed.
Production with call recordingSIP trunk with recording enabledTwilio recording: $0.0025/min. Worth it for debugging.
Multiple countriesMulti-region Twilio or TelnyxOne trunk per country. DNS-based routing per number.

What You Actually Configure

When setting up a voice agent, the Audio I/O configuration boils down to:

  1. Buy a phone number from Twilio or Telnyx — this is your DID (Direct Inward Dialing)
  2. Point it at your LiveKit server — configure the SIP trunk to route calls to your LiveKit instance's IP:5060
  3. Create a dispatch rule — map the phone number to an agent name (LiveKit handles room creation)
  4. That's it for Audio I/O. The rest of the pipeline (STT, LLM, TTS) is configured inside the agent, not at the I/O layer
Key takeaway: The Audio I/O layer's job is to deliver clean, correctly-timed audio frames to your STT engine. It doesn't know or care about transcriptions, prompts, or TTS voices. Those are downstream concerns. Get the I/O right and the rest of the pipeline has a fighting chance.

Check Your Understanding

  1. Why is SIP called a "signaling-only" protocol? What carries the actual audio?
  2. What sample rate is PSTN phone audio, and why does this matter for STT engine choice?
  3. When would you use WebRTC instead of SIP for a voice agent?
  4. What four things do you configure at the Audio I/O layer before a call can reach your agent?