Retrieving recordings and transcripts
After a call settles you can read its transcript and get a link to its recording. This guide covers when the artifacts are available, the shape of a transcript and how to download recordings through short-lived links.
Before you begin
transcripts:read: GET/calls/{call_id}/transcriptrecordings:read: GET/calls/{call_id}/recordingcalls:read: Reading the call itself to seeartifacts
Artifact scopes are separate from calls:read on purpose: a key that only needs call outcomes does not have to see what was said. The requests below use the documented example call id; use the id of your call. Any test call to +12025550100 produces both artifacts.
When artifacts are ready
Artifacts are produced after the call ends. Each call reports their state in artifacts:
JSON
| State | Meaning | What to do |
|---|---|---|
pending | Not ready yet | Wait for call.settled, or read the call again later |
ready | Available | Read it from its endpoint |
absent | There is none for this call | Nothing to fetch (for example a call nobody answered) |
failed | It could not be produced | Nothing to fetch; contact support with the call id if it keeps happening |
The simplest rule: fetch artifacts after call.settled (or once settled is true when polling). In live mode they are usually ready within a minute of the call ending, sometimes several minutes later. In test mode they are ready about 30 seconds after the call is created. Transcripts are not available while a call is in progress (capabilities.live_transcript is false).
Which test-mode calls have which artifacts:
to | Transcript | Recording |
|---|---|---|
+12025550100, +12025550105, +12025550106 | ready | ready |
+12025550104 (answered, nobody spoke) | absent | ready |
+12025550101, +12025550102, +12025550103 (not connected) | absent | absent |
Transcript
GET/calls/{call_id}/transcript returns the conversation turn by turn, verbatim. Nothing is masked: it is what was said.
Complete runnable example
Print a call's transcript, turn by turn.
The same steps as a complete script, with error handling, tested against test mode on every docs build. Each script reads your test key from an environment variable named in its first lines.
- cURL script (bash)
get-transcript.sh - JavaScript (Node.js 20+)
get-transcript.mjs - Python 3.9+ (requests)
get-transcript.py
The response (200):
JSON
speakerisagentorcaller(the person called). Open enum.indexorders the turns from 0.start_msandend_msare milliseconds from the start of the call, ornullwhen not known.turnsis empty unlessstateisready. A transcript that is stillpendingis answered with200andstate: "pending", not an error.simulatedistruefor test-mode fixture transcripts. Do not import them into production systems.
Recording
Preview The recording endpoints are in preview and may change.
GET/calls/{call_id}/recording returns the recording's state and, when it is ready, a short-lived HTTPS link to the audio.
First read the recording to get a fresh link:
Then, while state is ready and before expires_at, download the audio from its url. The link needs no API key (see the response below for its shape):
Complete runnable example
Download a call's recording to a local file.
The same steps as a complete script, with error handling, tested against test mode on every docs build. Each script reads your test key from an environment variable named in its first lines.
- cURL script (bash)
download-recording.sh - JavaScript (Node.js 20+)
download-recording.mjs - Python 3.9+ (requests)
download-recording.py
The response (200):
JSON
How the link works:
urlis valid for about 15 minutes, untilexpires_at. After that it answersmedia_link_expired(410); call the recording endpoint again for a fresh link.- The link needs no API key: the token in the path is the credential. It is tied to the key that obtained it: it stops working (
404) as soon as that key is revoked or expires, losesrecordings:read, or its creator leaves the workspace, even beforeexpires_at. - Until then it works for anyone who has it. Treat it like a password. Do not log it, store it, put it in a URL you share, or send it to a browser you do not control. Download the audio on your server and serve it from your own storage if people need to play it.
content_typeisaudio/wavfor test-mode audio and is usuallynullin live mode, where the format is known only when you download it: the download'sContent-Typeheader is authoritative.- The download supports
Rangerequests, so a player can seek. A range that cannot be served is answered with the whole file (200). - In test mode the audio is a short synthetic file and
simulatedistrue.
If you need to keep recordings, copy them into your own storage promptly and apply your own retention rules.
Handling call content
Transcripts and recordings are personal data of the person you called: their voice and what they said. RelationsAI returns them to your workspace in full. Store and share them according to your own data-protection obligations and your agreements with the people you call: restrict who can read them, keep them only as long as you need, and do not copy them into logs, analytics tools or tickets. See Security and data handling.
What can go wrong
state: "pending": The call has not settled yet. Fix: Wait forcall.settledand read again.state: "absent": The call never connected, or nobody spoke (transcript). Fix: Nothing to fetch; checkoutcomeon the call.media_link_expired(410) on download: The link passedexpires_at. Fix: Get a fresh link and download straight away.insufficient_scope(403): The key lackstranscripts:readorrecordings:read. Fix: Ask an admin for a key with that scope.resource_not_found(404): The call id is from the other mode or another workspace. Fix: Use the key that created the call.