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 & path | Scope | Purpose |
|---|---|---|
GET /calls/{callId}/recordings | recordings:read | Every recorded leg of a call |
GET /calls/{callId}/recordings/{uniqueId} | recordings:read | One 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.
| Field | Meaning |
|---|---|
uniqueId | Identifies the leg. Pass it to the single-leg endpoint to re-fetch just this one. |
callId | The 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. |
startTime | When the leg started, RFC 3339 UTC. |
durationSeconds | The leg's full length, including ringing. |
talkTimeSeconds | The connected portion only. This is the closer match to the length of the audio. |
from, to | The 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
Authorizationheader 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
302to 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
| Status | type | Cause |
|---|---|---|
400 | invalid_request | callId is not in the form 1754489201.884213. The message shows the expected shape. |
401 | unauthenticated | Missing or invalid credential. |
403 | insufficient_scope | The credential lacks recordings:read. |
403 | permission_denied | Either the account requires re-verification (below), or — for an OAuth token — the authorizing user does not have recording access. |
404 | not_found | No 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
recordingUrlon the event — no second call needed.