Every call is recorded by default. Recordings live in our storage for 90 days and are deleted automatically. Tenants can disable recording globally; two-party-consent state callers always hear the disclosure regardless.
How recording works
- When the call answers, our voice worker tells the carrier to start a dual-channel MP3 recording (caller / AI separated).
- When the carrier finishes encoding, a
call.recording.savedwebhook fires. - We stream the MP3 from the carrier into our storage at
recordings/<tenant_id>/<call_id>.mp3. calls.recording_r2_keyis set, and the recording shows up in the portal.- Bucket lifecycle deletes the object 90 days later.
Recording failures are non-fatal — the call still goes through, the transcript and summary still land, and the portal flags the call as has_recording: false.
TCPA disclosure
A short verbatim disclosure plays at the start of every recorded call, in the detected language:
- English: “Just so you know, this call is recorded and may be answered by an AI assistant…”
- Mexican Spanish: “Le informamos que esta llamada se está grabando…”
- Turkish: “Bilginize, bu çağrı kayıt altına alınmaktadır…”
The disclosure plays whenever any of the following is true:
- Tenant has
recording_default_enabled = 1(the default). - Caller’s area code maps to a two-party-consent state (CA, FL, IL, MA, MD, MT, NH, NV, PA, WA).
- Tenant’s stated state is in the consent list.
If you operate in a one-party-consent state and want to suppress the disclosure for non-consent-state callers, contact support to flip recording_default_enabled = 0.
Playback API
GET /v1/calls/:id/recording
Returns a 15-minute, tenant-scoped playback URL.
{
"url": "/v1/calls/01HXY3M0KEXAMPLECALLID/recording/stream?exp=1735689600",
"expires_at": 1735689600,
"ttl_seconds": 900
}
Hit the URL with the same Authorization: Bearer header to stream the MP3. Content-Type: audio/mpeg. Cache headers force private, no-store so browsers don’t cache call audio across sessions.
Disabling recording per tenant
# Operator-only, X-Internal-Secret authenticated. Contact support to flip.
UPDATE tenants SET recording_default_enabled = 0 WHERE id = '<tenant_id>';
A future /v1/billing/voice/recording endpoint will let tenants self-serve this; until then it’s gated to keep the TCPA decision deliberate.
Privacy on handoff
When the AI transfers a call to a human, we close our transcription socket immediately. The carrier keeps recording the bridged human leg, but only the AI portion of the conversation lands in calls.transcript. The recording still includes the full call by default — set tenants.record_after_handoff = 0 (the default) to stop the recording at the moment of transfer.
Lifecycle + deletion
- 90-day TTL is enforced at the storage bucket level, not in application code. There is no way to override it from a tenant API call.
- Tenant account deletion purges every recording within 30 days as part of the standard data-deletion policy.
- Court orders requesting longer retention are handled out-of-band per the Trust → Compliance page.