sid. The caller is on the left channel. The agent (AI or human) is
on the right channel. Because the two speakers land on separate channels,
you can run each side through its own transcript or scorer without a speaker-ID
model. After a call ends, the pipeline uploads the recording to your workspace’s
object storage. It stamps the URL onto the call’s CDR. The control plane can
then give you a time-limited download link.
How it works
1
Capture (automatic once enabled)
The media plane taps both RTP legs. It mixes them into a stereo stream
(caller = L, agent = R). It streams the result to the recording pipeline.
No SDK call is involved.
2
Finalize
On hang-up, the pipeline writes a stereo WAV to your workspace’s object
storage. It stamps
recording_url and the byte size onto the call’s CDR.3
Fetch
Read the CDR to find the recording. Then mint a short-lived presigned
download URL keyed on the
sid.Find recordings and get a download URL
The recording URL is stored on the CDR, not on the liveCall object. For this
reason, you fetch it through the control-plane API, not through the audio SDK.
Two routes do the job. cdr.recordings lists calls that produced a recording.
storage.signedUrl returns a one-hour presigned download link for a given
sid. Both routes authenticate with your API key.
- TypeScript
- Python
The presigned URL expires in one hour. Mint it when you are about to download,
not when you list. Requests are tenant-scoped. You can only presign recordings
that belong to your
orgId, even if you guess another workspace’s sid.React to a finished recording (native SDK cores)
The native SDK cores (Python, Go, Rust, and the other FFI wrappers) send aRECORDING_READY call event when the file is finalized. The event carries
recording_url and duration_seconds. With it, you can process recordings as
they complete instead of polling the CDR.
The native cores expose this event stream. The browser/TypeScript voice SDK
has no recording-event surface. From a browser or Node app, use the CDR +
storage.signedUrl path above.Analyze: split the channels
The download is a stereo WAV. The caller is on the left channel and the agent is on the right channel. Split the two channels to get a clean per-speaker signal. This channel split is your speaker separation. The recording path has no built-in diarization or voiceprint. Run each channel through your own ASR, or any scorer, independently.- TypeScript
- Python
Speaker separation here comes entirely from the stereo channel split. The
voice path has no speaker-ID, diarization, or voiceprint model. If both speakers
were mixed to mono, you could not tell them apart. The two-channel recording is
what makes per-speaker analysis reliable.
Pair it with call quality
Every recorded call also has objective quality data on its CDR: an ITU-T E-model MOS estimate, plus jitter and packet loss. The media plane computes these values. Join them to the recording when you score a call. Then you can attribute a low transcript confidence to a bad line rather than a bad agent. See MOS, jitter & loss for the fields and how the system derives them.Related
MOS, jitter & loss
The per-call quality metrics that live alongside the recording URL.
Call traces
Follow a single
sid across signalling, media, and agent turns.Stream audio to ASR
Transcribe live instead of after the fact. Feed uplink into your recognizer.
Observability overview
Recordings, CDRs, dashboards, and how they fit together.

