Skip to main content

Call recordings

Get playable links to the audio of a recorded call.

  • Base path: https://api.account.telebroad.com/api/public/v1
  • Auth header: Authorization: Bearer ACCESS_TOKEN
  • Scope: recordings:read

:::warning This is conversation content A recording link plays what people actually said. Treat the URLs as secrets and the audio as regulated data — in many jurisdictions and industries it is. Request recordings:read only if your product genuinely needs the audio. :::

Endpoints

Method & pathScopePurpose
GET /calls/{callId}/recordingsrecordings:readEvery recorded leg of a call
GET /calls/{callId}/recordings/{uniqueId}recordings:readOne leg

callId looks like 1754489201.884213 — a unix timestamp, a dot, a sequence number. It is the id you get from call history, from reports, and from the callId field on a call webhook.

List a call's recordings

curl https://api.account.telebroad.com/api/public/v1/calls/1754489201.884213/recordings \
-H "Authorization: Bearer $ACCESS_TOKEN"
{
"data": [
{
"uniqueId": "1754489201.884213",
"callId": "1754489201.884213",
"url": "https://api.account.telebroad.com/api/v1/recordings/x7Kd2p9QmR4tLwZ.mp3",
"startTime": "2025-08-06T14:06:41Z",
"durationSeconds": 95,
"talkTimeSeconds": 71,
"from": "12125550188",
"to": "104"
}
]
}

A call is not one recording

A call that rang a queue, was answered, and was then transferred has several legs, and each answered leg is recorded separately. That is why this is a list.

FieldMeaning
uniqueIdIdentifies the leg. Pass it to the single-leg endpoint to re-fetch just this one.
callIdThe switch's call id for that leg. On a transferred call it differs from the id in your request path — the request id names the whole journey, this one names the leg. Log it if you correlate against raw switch data.
startTimeWhen the leg started, RFC 3339 UTC.
durationSecondsThe leg's full length, including ringing.
talkTimeSecondsThe connected portion only. This is the closer match to the length of the audio.
from, toThe leg's endpoints as the switch recorded them. On an internal leg these are extensions, not phone numbers.

Picking "the" recording of a transferred call is a judgement only you can make — usually the leg with the largest talkTimeSeconds. Everything needed to make it is in the response, rather than hidden behind a server-side guess.

The url field

url is a signed link on this platform, not the storage location.

  • No Authorization header needed. Hand it to an <audio> element, a browser tab, or a transcription service directly.
  • The account is sealed into the signature. The link cannot be edited to reach another account's recording.
  • It does not expire. Anyone who has it can play the conversation. Do not put it anywhere a third party logs URLs.
  • It answers 302 to storage or streams the audio, depending on where the recording lives. Either way, just follow it.

Storage URLs are deliberately never returned. Recordings live in cloud storage for some accounts and on the PBX for others — the PBX ones have no URL at all and must be fetched through the switch. The signed link is one stable shape over both.

Get one leg

curl https://api.account.telebroad.com/api/public/v1/calls/1754489201.884213/recordings/1754489201.884213 \
-H "Authorization: Bearer $ACCESS_TOKEN"

Returns a single object rather than an array, or 404 if that leg of that call has no recording.

No recording yet

A call that exists but was not recorded returns 200 with an empty array:

{ "data": [] }

That is a normal answer — recording is opt-in per number and per user. It is distinct from 404, which means the call id is not on your account or does not exist.

Recordings are written after hangup and typically appear within a minute. If you are polling for one, treat [] as "not yet" and retry with backoff; do not treat it as "never".

Errors

StatustypeCause
400invalid_requestcallId is not in the form 1754489201.884213. The message shows the expected shape.
401unauthenticatedMissing or invalid credential.
403insufficient_scopeThe credential lacks recordings:read.
403permission_deniedEither the account requires re-verification (below), or — for an OAuth token — the authorizing user does not have recording access.
404not_foundNo such call on this account, or no such recorded leg on that call.

A call id belonging to another account returns 404, not 403. Guessing ids tells you nothing.

Two gates the scope does not open

Holding recordings:read is necessary but not always sufficient.

1. Sensitive-content verification. If the account has turned on re-verification for sensitive content, recordings require a password re-entry within the last 15 minutes. That means a signed-in human at a browser — there is nothing an API credential can present. While that setting is on, this endpoint returns 403 for every credential, and the message says so. Turning it off is an account decision, made in the portal.

2. The authorizing user's own permission (OAuth only). An OAuth token acts as the person who authorized it. If their role does not grant call-recording access in the portal, the token does not get it either — a scope narrows what a delegated app may do and can never widen it beyond its user. API keys are not affected: a key is the account's own credential for its own recordings.

Limits and caveats

  • Read-only. Deleting a recording is irreversible and is restricted to the account owner in the portal. There is no API for it, deliberately.
  • Voicemail is not here. A voicemail is a message, not a recording of a conversation, and it is stored separately. Mailbox legs are excluded from these results.
  • One call per request. There is no bulk endpoint. To sweep a date range, list calls in reports and fetch recordings per call id.
  • If you only need "was this recorded", the call webhooks already carry a recordingUrl on the event — no second call needed.