Everything is keyed by
call_sid — the same id the CDR, the transcript,
the usage rows, and (for an external agent) ctx.job.id all use. That is the
join key across every surface.
Authenticate
Use an org-scopedmpk_… key, minted in the console under Settings → API
keys. Scopes are per-operation and narrow: a key minted to fetch recordings
cannot place a call.
- Admin SDK (TypeScript)
- Raw HTTP
List recordings
number
default:"200"
1–500 rows per page.
number
default:"0"
Pagination offset.
number
Epoch milliseconds, inclusive, on call start.
"inbound" | "outbound"
string
Case-insensitive substring on the CDR’s agent attribution.
string
Case-insensitive substring over
call_id, caller, and callee.stats describes exactly the filtered set the rows come from —
total_recordings, total_duration, total_bytes, avg_duration — so a
dashboard’s KPI cards and its grid can never disagree.
Two eras, merged for you. Recordings written before 2026-06-03 carry a
cdrs.recording_url; everything since is a storage object discovered by
listing your tenant prefix. The operation merges both, one row per call,
newest-first. You do not need to know which era a call belongs to.Download one recording
recording_url is an internal s3:// locator. To read the audio, mint a
presigned GET — valid for one hour:
call_sid returns NOT_FOUND
rather than a URL. Objects in the shared fallback bucket are additionally
proven against your CDR rows before a URL is minted.
Files are WAV (or Opus) named <call_sid>.<ext>, both legs mixed.
Get pushed instead of polling
Subscribe tovoice.recording.ready and the platform tells you the moment the
upload completes — no list-and-diff loop. Add the Transcripts & recordings
event group to a webhook endpoint in the console (it carries both
voice.transcript.ready and voice.recording.ready).
call_id and fetch. Webhook deliveries are signed — verify
X-Clutchcall-Signature (t=…,v1=…, HMAC over "<t>.<body>") before you
trust one. See Webhooks.
Storage and retention
storage.info reports what your tenant is holding and the retention window:
Recording an external agent’s calls
Recording is a call-layer capture (the RTP tap), not an agent-pipeline feature. A call handled by your ownlivekit-agents worker over our transport
is therefore recorded exactly like a native agent’s call — both legs, same
storage, same API. Nothing in your agent needs to participate.
Correlate it from inside your agent by logging ctx.job.id, which is the same
call_sid this API is keyed by.
Related
Webhooks
Every event type, the signature scheme, and retry behaviour.
Admin API SDK
The typed client, the key model, and the full operation surface.
Observability
Where recordings sit beside CDR, MOS, and transcripts.
LiveKit Agent over TeleQuick Transport
What an external agent does and does not get automatically.