Fetch the recording resource for a call (redirect, JSON, or status)
Returns the recording asset for the given call in one of four ways depending on the recording's lifecycle state and the requested response mode:
-
302 (default) — recording is ready;
Locationheader carries a 15-minute presigned Sautikit CDN URL. The response body is empty. The client can passLocationdirectly to<audio src=...>— audio bytes do NOT flow through the API process. The Sautikit URL is the only URL ever surfaced; the raw PBX URL is never exposed. -
200 — JSON mode: send
Accept: application/jsonor?format=jsonand a ready recording is returned as JSON metadata instead of the redirect.url/recording_urlcarry the same presigned Sautikit URL the 302 would have used (15-minute TTL), alongsidesize_bytes,duration_seconds,mime/content_type, andexpires_at. Prefer this mode from browsers: a cross-originfetchcannot follow the 302 (the redirect hop fails CORS) — fetch the JSON and handurlto<audio src=...>instead. -
202 — recording capture is still in progress (
status: pending). The client should waitretry_after_secondsbefore re-polling (also reflected in theRetry-Afterresponse header). Error code:recording_not_ready. -
410 — the asset has been pruned per the workspace's storage-tier retention policy and is permanently unavailable. Error code:
recording_unavailable. -
404 — the call does not exist, belongs to a different workspace, or was never configured for recording. Cross-tenant existence is not leaked. Error code:
recording_not_found.
Workspace isolation: call_id must belong to the active workspace;
otherwise the endpoint returns 404.
call_id may be the Sautikit call UUID or the PBX session id
(HD_…) the same call carries on the wire — a caller that watched
the call happen can fetch its recording without looking the UUID
up first.
Authorization
bearerAuth Long-lived ES256 JWT minted from the dashboard (https://app.sautikit.com/developers/api-keys). Signed by the
platform keyring. Carries workspace_id and scopes claims;
revoked via the platform deny-list.
In: header
Path Parameters
The call's Sautikit UUID, or the PBX session id the same call
carries on the wire (HD_…, returned as session_id on
POST /v1/calls and on every webhook). Either form addresses
the same call.
Query Parameters
Set to json to force JSON mode (equivalent to Accept: application/json) — returns 200 metadata instead of the 302 redirect when the recording is ready.
Value in
- "json"
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/calls/string/recording"{ "url": "https://storage.sautikit.com/recordings/9d2b1f53/be36758c.wav?X-Amz-Signature=abc123", "recording_url": "https://storage.sautikit.com/recordings/9d2b1f53/be36758c.wav?X-Amz-Signature=abc123", "size_bytes": 1048576, "duration_seconds": 45, "mime": "audio/wav", "content_type": "audio/wav", "expires_at": "2026-07-22T10:31:57Z"}{ "status": "pending", "retry_after_seconds": 10}{ "error": { "code": "validation.bad_request", "message": "string", "request_id": "string", "details": [ "string" ] }}{ "error": { "code": "recording_not_found", "message": "call recording not found", "request_id": "req-a1b2c3d4" }}{ "code": "recording_unavailable", "reason": "recording pruned after 30-day retention window"}Submit voice actions for a control event (MCP live call control) POST
Answers a `response_required` event from `GET /v1/calls/{call_id}/control/events` with a voice-actions batch (same schema as `validate_voice_actions`). Each event can be answered exactly once.
Mint a short-lived WebRTC PBX token for the browser softphone POST
Requests a workspace-scoped WebRTC bearer token from the PBX. The token is returned as-is from the PBX; the caller passes it to the in-browser softphone SDK to register. Short-lived (typically 60–300 s); renew before expiry. Optional body fields (`client_name`, `role`) are forwarded to the PBX and reflected in token claims; both may be omitted. Every body field is optional, but the request body itself must be valid JSON — send at least `{}` (an empty body is rejected 400 `validation.bad_request`). To bind the token to one of your DIDs (so the WebRTC session routes through that number), pass **either** `tenant_number_id` (the internal number id) **or** `phone_number` (the DID in E.164). The backend resolves the number's PBX sip-profile and embeds it as the token's `sip.id` claim. Omit both to mint a non-DID-bound token, in which case the PBX assigns `sip.id` `"default"`. When both are supplied, `tenant_number_id` wins.