Whether a call is recorded follows the recording policy (set by the Unpod team). To
change it, ask them.
record=True on a call doesn’t override the policy today, and the
record setting in a number’s inbound config is stored but not applied yet.Get a recording
Export your credentials (UNPOD_BASE_URL,
UNPOD_PLATFORM_TOKEN, UNPOD_ORG_HANDLE). After the call ends, get_recording returns
the recording’s details. Until the recording is saved it raises 409 (the call is still
going) or 404 (it has just ended), so wait a few seconds and ask again. A wrong
call_id also returns 404, so it only fails once the retries run out:
An unknown
call_id, or one from another org, returns 404 not_found. After hangup,
GET /sbc/v1/calls/{id} returns the same details, but its url is a path inside Unpod:
download from the url that get_recording returns.
Download the audio
Request the same URL with the headerAccept: audio/wav, plus the same Authorization
and Org-Handle headers as every other API call. Without Accept: audio/wav you get the
JSON above. The file is about 1 MB per minute of call.
200 means call.wav holds the audio. A call with no recording returns 404, and
call.wav then holds the error instead. The URL only works with your credentials, so
download the file and serve it from your own storage rather than handing the URL to a
browser.
Format
The first leg (A) is on the left channel and the second leg (B) on the right.
To get each person in their own file, split the channels with sox or ffmpeg:
-c:a pcm_alaw with ffmpeg (sox keeps
the input encoding by itself). Python’s wave module can’t open G.711 WAV files (it fails
with unknown format: 7 for μ-law, 6 for A-law), and audioop is gone from Python
3.13. If a tool needs 16-bit PCM, convert the file first with sox or ffmpeg:
sox call.wav -b 16 -e signed-integer call-pcm.wav.
Recording events
When a recorded call ends, Unpod sendsMPCRecordingCompleted to the call’s
recording_callback_url only, never to status_callback_url. Set it on create, or in a
number’s inbound config. The event
carries ev.call_id, ev.recording_url and ev.recording_duration. Treat it as a hint
that the recording is ready:
- It isn’t signed yet, so
parse_call_eventwith your secret rejects it. Parse it withverify=Falseand trust only whatget_recordingreturns forev.call_id. - It carries no
metadata, so match it to your own records byev.call_id. ev.recording_urlis a path inside Unpod, not a download link. Callget_recording(ev.call_id)for theurl.- It may never arrive for a call started with
create. The reliable way is to callget_recordingafter the call’sHangup, as above.
/recording route in Number Masking
handles the event this way.
With record_participant_track=True (which needs record=True), the same URL also gets
ParticipantRecordingInitiated and ParticipantRecordingCompleted for each leg. Both
arrive together after the call ends, and both carry the leg’s channel in
ev.metadata["channel"]: 1 for the first leg (left), 2 for the second (right). Only
ParticipantRecordingCompleted carries the recording, in ev.metadata["recording_url"]:
the same 2-channel file, at the same path inside Unpod.
Answer URL <Record>. When a <Record> in your
answer-URL reply reaches its maxLength (300 s by default),
Unpod sends RecordStop to your answer URL with ev.recording_url. That’s the same path
inside Unpod: once the call ends, get the audio with get_recording(ev.call_id).
Record part of a call
calls.record asks Unpod to start recording a call that’s still going:
404 not_found. A call
the recording policy already records keeps that one recording. Whether get_recording
returns a recording started this way isn’t confirmed yet. transcribe=True is accepted,
but no transcript is made yet.
Tell callers
Say it at the start of the call. On an inbound call, put a<Speak> first in your
answer-URL reply:
create,
speak the same line when the leg
sends Answered.
If the recording policy records the call, you don’t need <Record/>. Never put <Record/>
before <Dial>: Unpod waits for the recording to reach its maxLength (300 s by default)
before dialing, and the caller hears silence.