GET /calls/{callid}/transcripts
Get a call's transcripts
Every transcript for a call — one per recorded leg, not one per call.
| Scope | recordings:read |
| Role permission | Call reports → allow recordings |
| Cost | Free |
Why recordings:read and not reports:read: a transcript is the
conversation written down, so it carries the same privacy weight as the
audio. The platform gates both on the same role permission, so they share
one scope — a consent screen offering "transcripts but not recordings"
would be describing a separation that does not exist.
A transferred call produces several recordings and therefore several
transcripts, all sharing this call id. Pair them to legs with
recording, which embeds the leg's uniqueId.
200 with [] is normal — the call was not recorded, was too short,
or transcription has not finished. It does not mean the call is missing.
Path parameters
| Name | Type | Required | Example | Description |
|---|---|---|---|---|
callid | string | yes | 1754489201.884213 | The call id from call history, reports or a call webhook — a unix timestamp, a dot, and a sequence number. A malformed id is a 400 that names the expected shape, not a 404. |
Responses
200
The call's transcripts. Empty when there are none.
| Field | Type | Description |
|---|---|---|
[].id | integer | |
[].callId | string | Shared by every transcript of the call. |
[].recording | string | The audio file this was transcribed from, and the reliable way to tell which leg it is: the name embeds the leg's uniqueId with the dot removed — 1785979165.372492 becomes record_1785979165372492_93458.wav. Without it the only option is pairing transcripts to legs by position and hoping the orders agree. |
[].segments | object[] | The diarized utterances in time order. Empty when the audio produced no transcript. |
[].segments[].start | number | Seconds from the start of the audio. Fractional. |
[].segments[].end | number | Seconds from the start of the audio. Fractional. |
[].segments[].speaker | integer | A diarization index, not an identity. 0 and 1 distinguish voices, and the mapping is not stable between calls. Use the transcript's agentSpeaker to tell which one is the agent. |
[].segments[].text | string | |
[].shortSummary | string | Absent when summarization did not run, which is normal rather than an error. |
[].bulletPoints | string[] | |
[].callPurpose | string | Per-account vocabulary, not a fixed enum. |
[].callSentiment | string | Per-account vocabulary, not a fixed enum. |
[].customerSatisfactionScore | integer | Absent is not zero. A missing score means the call was never analyzed; zero would mean it scored nothing. Averaging nulls as zeros is the most common way these numbers get misreported. |
[].customerSatisfactionScoreNote | string | |
[].agentPerformanceScore | integer | Absent when not scored — see customerSatisfactionScore. |
[].agentPerformanceScoreNote | string | |
[].actionRequired | boolean | |
[].actionRequiredNote | string | |
[].urgentActionRequired | boolean | |
[].urgentActionRequiredNote | string | |
[].agentName | string | |
[].agentSpeaker | integer | Which diarized speaker is the agent. Absent when the analysis could not tell them apart — do not fall back to assuming speaker 0. |
[].customKpis | object[] | |
[].customKpis[].name | string | The KPI as your account defined it — these are per-account. |
[].customKpis[].value | integer | Absent means the KPI was not scored on this call, which is not the same as scoring zero. |
[].customKpis[].note | string | The model's reasoning, when it gave one. |
{
"data": [
{
"id": 44219,
"callId": "1754489201.884213",
"recording": "record_1754489201884213_93458.wav",
"shortSummary": "Caller asked to reschedule Tuesday's roof inspection to 9am.",
"bulletPoints": [
"Reschedule requested",
"Agent confirmed the new slot"
],
"callPurpose": "scheduling",
"callSentiment": "positive",
"customerSatisfactionScore": 9,
"agentPerformanceScore": 8,
"actionRequired": true,
"actionRequiredNote": "Update the crew calendar.",
"urgentActionRequired": false,
"agentName": "Dana Spencer",
"agentSpeaker": 1,
"segments": [
{
"start": 0.42,
"end": 3.1,
"speaker": 0,
"text": "Hi, can we move Tuesday to nine?"
},
{
"start": 3.4,
"end": 6.85,
"speaker": 1,
"text": "Sure, nine works. I will update the crew."
}
]
}
]
}
Errors
| Status | Meaning |
|---|---|
400 | invalid_request — a missing or malformed field, an unknown enum value, or an unknown field in the body. Nothing was charged. |
401 | unauthenticated — no credential, or one that is invalid, revoked or expired. The WWW-Authenticate header names the scope the endpoint wanted. |
403 | Two different failures share this status, and the type tells them apart: |
404 | not_found — no such object, or it is not this account's. The two are deliberately indistinguishable. |
500 | internal_error — something failed on our side. For a send, nothing was charged, guaranteed, which is what makes a retry safe. |
See Errors for the full catalog and what to do about each.
Example
curl -X GET 'https://api.account.telebroad.com/api/public/v1/calls/1754489201.884213/transcripts' \
-H "Authorization: Bearer $TB_KEY"