Signals Docs
Guides

Recording calls

This guide covers capturing audio from a call with the <Record> verb, receiving the result on your server, downloading the audio and reading recording metadata through the Calls API.

Record a voicemail message

The most common pattern prompts the caller, then records a message with <Record>. When the caller stops speaking, presses #, or the recording reaches its maximum length, Signals requests the action URL with the result.

const express = require("express"); const app = express(); app.use(express.urlencoded({ extended: false })); app.post("/voice/incoming", (req, res) => { const oneml = `<?xml version="1.0" encoding="UTF-8"?> <Response> <Say>Leave a message after the beep. Press the pound key when you are finished.</Say> <Record action="/voice/voicemail-complete" maxLength="120" finishOnKey="#"/> <Say>We did not receive a recording. Goodbye.</Say> <Hangup/> </Response>`; res.type("text/xml").send(oneml); }); app.post("/voice/voicemail-complete", (req, res) => { console.log("Voicemail received:", { recordingSid: req.body.RecordingSid, recordingUrl: req.body.RecordingUrl, durationSeconds: req.body.RecordingDuration, }); const oneml = `<?xml version="1.0" encoding="UTF-8"?> <Response> <Say>Thank you. Your message has been recorded. Goodbye.</Say> <Hangup/> </Response>`; res.type("text/xml").send(oneml); }); app.listen(3000);

Signals posts RecordingSid, RecordingUrl and RecordingDuration to action. See Record for every attribute, including timeout, finishOnKey and maxLength.

Get notified when a recording finishes

Some recordings are not tied to a single action, for example when you log a whole call for quality review. Set recordingStatusCallback instead of, or alongside, action to have Signals notify a separate URL once the recording finishes:

XMLCode
<?xml version="1.0" encoding="UTF-8"?> <Response> <Say>This call may be recorded for quality purposes.</Say> <Record playBeep="false" timeout="0" maxLength="3600" recordingStatusCallback="/voice/recording-status"/> <Hangup/> </Response>

Signals requests recordingStatusCallback once, for the completed event only. The request carries these parameters:

ParameterDescription
AccountSidYour Voice Account SID.
CallSidUnique identifier for the call.
RecordingSidUnique identifier for the recording.
RecordingUrlURL of the recording audio on the Calls API.
RecordingStatusAlways completed.
RecordingDurationDuration of the recording, in seconds.
RecordingChannelsNumber of audio channels in the recording.
RecordingSourceRecordVerb for <Record>, DialVerb for a recorded <Dial>.

The receiver only needs to acknowledge the request. It does not control the call, because <Record> has already handed the call to the next verb:

const express = require("express"); const app = express(); app.use(express.urlencoded({ extended: false })); app.post("/voice/recording-status", (req, res) => { console.log("Recording completed:", { recordingSid: req.body.RecordingSid, recordingUrl: req.body.RecordingUrl, durationSeconds: req.body.RecordingDuration, }); res.sendStatus(200); }); app.listen(3000);

Download recording audio

RecordingUrl points at the recording's .wav resource on the Calls API:

Code
https://api.signals.pressone.africa/2010-04-01/Accounts/ACCOUNT_SID/Recordings/RECORDING_SID.wav

Downloading it requires the same HTTP Basic credentials as the rest of the Calls API. Signals checks that the recording belongs to the project that owns your Voice Account. Anyone without your auth token gets 401.

curl -u "ACCOUNT_SID:AUTH_TOKEN" \ -o voicemail.wav \ "https://api.signals.pressone.africa/2010-04-01/Accounts/ACCOUNT_SID/Recordings/REd947fbd985d13ecb4c31eca3186f20bb.wav"

The audio is served as audio/wav with Cache-Control: private, no-store. The resource also supports:

  • HEAD, to read Content-Length and ETag without downloading the file.
  • A single Range header, such as Range: bytes=0-1023, answered with 206 Partial Content. An unsatisfiable range returns 416.

Possible errors:

StatusCodeMeaning
401unauthenticatedThe username is not the path AccountSid, the Voice Account is not active, or the auth token is wrong.
404recording_not_foundThe recording does not exist, belongs to another project, or has passed its retention period.
409recording_processingSignals is still verifying the audio after the call. Retry shortly.
409recording_failedThe audio could not be verified and is not available.
429egress_cap_reachedYour project's recording download allowance is used up. detail states when it resets.

Do not send RecordingUrl with embedded credentials to a browser or mobile app. To play a recording to your own users, download it on your server and serve it from an endpoint your server authenticates.

Read recording metadata

With a CallSid, you can list the recordings captured during a call, and fetch any recording's metadata.

List every recording for a call

TerminalCode
curl -u "ACCOUNT_SID:AUTH_TOKEN" \ "https://api.signals.pressone.africa/2010-04-01/Accounts/ACCOUNT_SID/Calls/CA5388f7a5e04d0ed71cac5cdf1ea17a53/Recordings.json"

This list is not paginated, and entries carry no media URL. Build the download URL from each sid as shown in Download recording audio:

JSONCode
{ "recordings": [ { "sid": "REd947fbd985d13ecb4c31eca3186f20bb", "account_sid": "AC9ce1cd445308e3ec5080c6126288aa09", "call_sid": "CA5388f7a5e04d0ed71cac5cdf1ea17a53", "duration": 42, "format": "wav", "date_created": "2026-07-02T12:00:00Z", "date_updated": "2026-07-02T12:00:42Z", "uri": "/2010-04-01/Accounts/AC9ce1cd445308e3ec5080c6126288aa09/Recordings/REd947fbd985d13ecb4c31eca3186f20bb.json" } ], "uri": "/2010-04-01/Accounts/AC9ce1cd445308e3ec5080c6126288aa09/Calls/CA5388f7a5e04d0ed71cac5cdf1ea17a53/Recordings.json" }

Fetch a single recording

TerminalCode
curl -u "ACCOUNT_SID:AUTH_TOKEN" \ "https://api.signals.pressone.africa/2010-04-01/Accounts/ACCOUNT_SID/Recordings/REd947fbd985d13ecb4c31eca3186f20bb.json"

A completed recording returns its duration and format:

JSONCode
{ "sid": "REd947fbd985d13ecb4c31eca3186f20bb", "account_sid": "AC9ce1cd445308e3ec5080c6126288aa09", "call_sid": "CA5388f7a5e04d0ed71cac5cdf1ea17a53", "duration": 42, "format": "wav", "date_created": "2026-07-02T12:00:00Z", "date_updated": "2026-07-02T12:00:42Z", "uri": "/2010-04-01/Accounts/AC9ce1cd445308e3ec5080c6126288aa09/Recordings/REd947fbd985d13ecb4c31eca3186f20bb.json" }

If you fetch a recording before it finishes, the response omits duration and format entirely rather than returning them as null. An unknown RecordingSid returns 404.

Signals call history and recordings are also available through the Signals API. See the Calls and recordings API reference.

Next steps

Last modified on