Calls, Recordings, and Participants
Every call Sent handles for you is a record under /v3/calls. This guide covers reading that record, ending a live call, recording it, and managing the participants of a call your callback connected to a conference room.
Prerequisites
- A call id. It arrives as
callIdon thecall.requestquestion your callback receives, and ascall_idon everycall.*webhook. See Answering Calls. - A number with phone calls turned on, with a callback URL that answers. See Managing channels.
All requests authenticate with the x-api-key header. Call ids look like call_9f2ab000-0000-4000-8000-000000000001; anything else is refused with 400.
The Call Record
GET /v3/calls/{id} returns one of your calls, with a timeline of when it entered each status:
curl "https://api.sent.dm/v3/calls/call_9f2ab000-0000-4000-8000-000000000001" \
-H "x-api-key: $SENT_API_KEY"{
"success": true,
"data": {
"id": "call_9f2ab000-0000-4000-8000-000000000001",
"direction": "outbound",
"from": { "kind": "user", "value": "agent-42" },
"to": { "kind": "number", "value": "+12025550142" },
"number": "+12025550123",
"status": "COMPLETED",
"failure_reason": null,
"started_at": "2026-07-22T12:00:00+00:00",
"answered_at": "2026-07-22T12:00:05+00:00",
"ended_at": "2026-07-22T12:00:47+00:00",
"duration_seconds": 42,
"price": null,
"recording_available": false,
"timeline": [
{ "status": "INITIATED", "timestamp": "2026-07-22T12:00:00+00:00" },
{ "status": "ANSWERED", "timestamp": "2026-07-22T12:00:05+00:00" },
{ "status": "COMPLETED", "timestamp": "2026-07-22T12:00:47+00:00" }
]
},
"error": null
}| Field | Description |
|---|---|
id | The call id. The same one the call.request question and every call.* webhook carry |
direction | outbound for a call placed from your app, inbound for a call to one of your numbers |
from, to | Who placed the call and who it was for. kind is user, number, conference or anonymous; value is the app user's identity, the phone number in E.164 format, or the room name. value is null when the kind is anonymous |
number | Your number that owns the call: the dialed number for an inbound call, the caller's bound number for a call placed from your app |
status | INITIATED, RINGING, ANSWERED, COMPLETED, FAILED, NO_ANSWER or REJECTED. Upper case, like a message status |
failure_reason | Why the call did not complete. Null while the call is live, when it completed, and when it failed without a recorded reason. See Call Failure Reasons |
started_at | When the call was placed (UTC) |
answered_at | When the call was answered (UTC). Null until then, and always null for a call between two of your app users |
ended_at | When the call ended (UTC). Null while the call is live |
duration_seconds | Billable duration in seconds. Null while the call is live |
price | What the call cost. Null until it has been priced. Only completed calls are charged |
recording_available | True once a recording of the call is available |
timeline | When the call entered each status, oldest first. Only returned when reading one call |
GET /v3/calls lists your calls, most recent first, in pages of page_size (default 20, at most 100) starting at page 1. Filter with direction, status, number (one of your voice numbers) and the time the call started: from and to, both inclusive, as ISO 8601.
curl "https://api.sent.dm/v3/calls?direction=inbound&status=completed&from=2026-07-22T00:00:00Z" \
-H "x-api-key: $SENT_API_KEY"Filters match any case, so status=completed and status=COMPLETED are the same filter. Use the call.* webhooks for live updates; the list is for looking calls up afterwards.
Hang Up a Call
POST /v3/calls/{id}/hangup ends one of your live calls:
curl -X POST "https://api.sent.dm/v3/calls/call_9f2ab000-0000-4000-8000-000000000001/hangup" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'The response is 204. The call then ends the way any other call does, once the disconnect is reported. An answered call ends COMPLETED and you receive call.completed. A call that was still ringing ends NO_ANSWER, REJECTED or FAILED, as the disconnect reports it, and you receive call.failed.
| Response | Meaning |
|---|---|
409 CONFLICT_008 | The call has already ended |
409 CONFLICT_011 | The call has no phone leg, such as a call between two of your app users. The app ends that call itself |
Record a Call
A recording starts in one of three ways:
record: trueon aconnectToNumberorconnectToConferenceanswer. Recording starts the moment the legs join.- A
startRecordinginstruction in an answer, before the action runs. POST /v3/calls/{id}/recordingswithactionstart, mid-call.
curl -X POST "https://api.sent.dm/v3/calls/call_9f2ab000-0000-4000-8000-000000000001/recordings" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "start" }'Stop the same way with action stop, or with a stopRecording instruction in a later answer. Either stops a recording started by any of the three. The response is 204.
Each start is a recording of its own. Once the call was recorded and the recording has landed, call.recording_ready is sent once per recording, naming it in recording_id. Until then, and for a call that was never recorded, the recordings list is empty.
GET /v3/calls/{id}/recordings returns every recording of the call, oldest first, each with a link:
curl "https://api.sent.dm/v3/calls/call_9f2ab000-0000-4000-8000-000000000001/recordings" \
-H "x-api-key: $SENT_API_KEY"{
"success": true,
"data": {
"recordings": [
{
"recording_id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
"download_url": "https://recordings.example.com/9f2ab000-0000-4000-8000-000000000001/7c1d2e3f4a5b4c6d8e9f0a1b2c3d4e5f.mp3?X-Amz-Expires=3600&X-Amz-Signature=...",
"url_expires_at": "2026-07-22T13:00:00+00:00"
}
]
},
"error": null
}| Field | Description |
|---|---|
recording_id | The recording's id, the one its call.recording_ready webhook announced it under |
download_url | A pre-signed link that downloads the recording as an MP3 file |
url_expires_at | When the link stops working (UTC). Request the recordings again for a fresh link |
Anyone holding download_url can download the recording until the link expires. Treat it like the recording itself: fetch it from your backend, and never hand it to a browser you do not control.
| Response | Meaning |
|---|---|
403 BUSINESS_026 | Call recording is not available. Applies to stop as well, because nothing could be recording |
409 CONFLICT_008 | The call has already ended |
409 CONFLICT_011 | The call has no phone leg, such as a call between two of your app users, which cannot be recorded |
A connectToUser answer accepts record: true but never honours it: a call connected to an app user cannot be recorded. The call connects without the recording. Stops in the same answer still run, because a recording started earlier in the call may still be running.
Conference Participants
Only a live call your answer connected to a conference room takes participants. A live call connected to a user or a number answers 409 CONFLICT_009; a call that has ended answers 409 CONFLICT_008.
POST /v3/calls/{id}/participants dials one of your app users or a phone number into the room:
curl -X POST "https://api.sent.dm/v3/calls/call_9f2ab000-0000-4000-8000-000000000001/participants" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": { "kind": "number", "value": "+14155551234" },
"caller_id": "+12025550123"
}'| Field | Description |
|---|---|
to.kind | user for one of your app users, number for a phone number |
to.value | The app user's identity, or the phone number in E.164 format |
caller_id | The number shown to a phone participant as the caller, in E.164 format. Must be one of your numbers. The call's owning number when omitted |
The response is 202 with the participant's own call record. from.kind is conference, because the room called them:
{
"success": true,
"data": {
"id": "call_9f2ab000-0000-4000-8000-000000000002",
"direction": "outbound",
"from": { "kind": "conference", "value": "daily-standup" },
"to": { "kind": "number", "value": "+14155551234" },
"number": "+12025550123",
"status": "INITIATED",
"failure_reason": null,
"started_at": "2026-07-22T12:00:00+00:00",
"answered_at": null,
"ended_at": null,
"duration_seconds": null,
"price": null,
"recording_available": false,
"timeline": null
},
"error": null
}A participant is a call of their own. It has its own id, can be read with GET /v3/calls/{id} and hung up, and is billed and reported through call.completed and call.failed like any other call.
Every participant needs a positive balance on the account. A phone participant also needs a destination you may call and a caller id you own:
| Response | Meaning |
|---|---|
400 BUSINESS_011 | Calls to the destination country are not allowed |
400 BUSINESS_022 | caller_id is not one of your numbers |
402 BUSINESS_003 | Insufficient balance. Checked for every participant, app users included |
409 CONFLICT_008 | The call has ended |
409 CONFLICT_009 | The call is live but not in a conference |
The room is recorded on the call as Sent reads your answer. A participant added right after the answer can briefly see 409 CONFLICT_009 while that write lands. Retry for a few seconds before treating it as final.
GET /v3/calls/{id}/participants lists who is in the room, the call itself included:
{
"success": true,
"data": [
{
"id": "call_9f2ab000-0000-4000-8000-000000000001",
"kind": "user",
"value": "agent-42",
"muted": false,
"duration_seconds": 184
},
{
"id": "call_9f2ab000-0000-4000-8000-000000000002",
"kind": "number",
"value": "+14155551234",
"muted": true,
"duration_seconds": 96
}
],
"error": null
}| Field | Description |
|---|---|
id | The participant's own call id. What the mute and remove endpoints take, and what GET /v3/calls/{id} accepts |
kind | user for one of your app users, number for a phone number, anonymous for a caller who withheld their number |
value | The app user's identity or the phone number in E.164 format. Null when the kind is anonymous |
muted | True while the room mutes this participant |
duration_seconds | How long the participant has been connected to the room, in seconds |
The list is empty once everyone has left.
PATCH /v3/calls/{id}/participants/{participantId} mutes or unmutes one participant. muted is required: true silences them, false lets them be heard again. Muting a participant who is already muted succeeds, as does unmuting one who is not. The response is 204.
curl -X PATCH "https://api.sent.dm/v3/calls/call_9f2ab000-0000-4000-8000-000000000001/participants/call_9f2ab000-0000-4000-8000-000000000002" \
-H "x-api-key: $SENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "muted": true }'DELETE /v3/calls/{id}/participants/{participantId} removes one participant. Their leg ends and is reported through call.completed; everyone else stays connected.
DELETE /v3/calls/{id}/participants removes every participant, the call itself included. Every leg ends and is reported through call.completed. A room that is already empty answers 204 as well.
Both answer 404 RESOURCE_017 for a participant who is not in this call's room, 409 CONFLICT_008 for a call that has ended, and 409 CONFLICT_009 for a live call that is not in a conference.
What a Call Costs
price is on the call record and on call.completed. On the webhook it is omitted until billing has recorded the charge, so read the call again later if you need it. Only completed calls are charged.
A leg to a phone number costs money for as long as it runs. It is told up front how long your balance affords at the destination's per-minute rate, and ends as completed when it reaches that cap. Legs to app users and conference rooms are free and have no such limit.
Next Steps
Call Events
call.initiated, call.answered, call.completed, call.failed and call.recording_ready
Answering Calls
The question Sent asks your callback and the answer that decides the call
Voice Recipes
Ring an app user with a phone fallback, a menu, a hold, a recorded call, a room per call, and routing by department
List Calls
Every filter and field of GET /v3/calls
Calls From Your App
How a user of your web app places and receives phone calls with Sent: mint voice tokens on your backend, register the browser with the Sent voice SDK, and route calls by department.
Voice Recipes
Six worked answers for your voice callback, taken from the Sent voice example app: ring an app user with a phone fallback, a basic IVR menu, a hold, a recorded call, a room per call, and routing by department.