Calls
Place outbound calls with POST /api/calls. Inbound calls go to the webhook URL set on the number in the dashboard. Either way your endpoint returns markup (XML, or JSON for a smaller verb set) telling Connect what to do. See call-control markup. You can fetch a call's record by its SID.
API stabilityConnect is live. The API is v0.x, so field names may change before v1. Base URL: https://connect.telvox.dev/api. Sample numbers are placeholders; outbound dialling is set up for Indian numbers.
Properties
Fetching a call returns its call log record with these fields.
| Property | Type | Description |
|---|---|---|
| call_sid | string | Unique ID for the call: CA plus 32 uppercase letters and digits. |
| from_number | string | The calling number. For outbound calls, the claimed number you passed as from. |
| to_number | string | The number that was called. |
| direction | enum | inbound or outbound. |
| status | enum | Where the call is now. See the status values below. |
| duration | integer | Call length in seconds, once the call has ended. |
| price | string | Cost of the call as a decimal string. Can be null. |
| price_unit | string | Currency of price. Can be null. |
| webhook_url | string | Where Connect fetched markup for this call: your url for outbound, the number's webhook URL for inbound. |
| status_callback | string | Where status callbacks for this call are sent. Can be null. |
| recording_url | string | The name of the call's recording (not a full URL). Pass it to GET /api/recordings/{recordingName}. Null if nothing was recorded. |
| recording_status | string | Latest recording state, for example recording, paused or stopped. Can be null. |
| recording_source | string | How the recording was made, for example bridge or channel. Can be null. |
| error_message | string | Why the call failed, when it did. Otherwise null. |
| start_time | string | ISO 8601 time the call entered Connect. |
| ringing_at | string | ISO 8601 time the far end started ringing. |
| answered_at | string | ISO 8601 time the call was answered. Null if it never was. |
| end_time | string | ISO 8601 time the call ended. |
| created_at | string | ISO 8601 time the record was created. |
| updated_at | string | ISO 8601 time the record last changed. |
| metadata | object | Internal trace data. It may change at any time, so do not depend on it. |
Status values
The status field moves through these values. The final ones (completed, busy, no-answer, failed, canceled) mean the call is over.
| Value | Type | Description |
|---|---|---|
| initiated | status | The call is being placed. POST /api/calls returns this. |
| ringing | status | The destination is ringing. |
| in-progress | status | The call was answered and is connected. |
| completed | status | The call was answered and then ended. |
| busy | status | The destination returned busy. |
| no-answer | status | The call rang but nobody answered. |
| failed | status | The call could not be connected (network or carrier error). |
| canceled | status | The call ended before it was answered. |
Create a call
Place an outbound call. When the callee answers, Connect POSTs to your url and runs the XML or JSON markup it returns.
/api/callsPlace an outbound call from one of your claimed numbers.
| Parameter | Type | Description |
|---|---|---|
| torequired | string | Destination number in E.164 (10 or more digits, + optional), or a SIP peer name on the platform. |
| fromrequired | string | A number you have claimed in the dashboard and that is active, in the same format shown there, for example +918045670100. Any other number returns 422. |
| urlrequired | string | Connect POSTs JSON {call_sid, to, from, direction} here when the call is answered. Reply with XML or JSON markup. |
| statusCallback | string | Where to POST status callbacks for this call. status_callback is also accepted. |
| statusCallbackEvent | array | An array or a space-separated string. ringing, in-progress and the final status are always sent. Add initiated to also get the initiated event. status_callback_event is also accepted. |
curl -X POST https://connect.telvox.dev/api/calls \
-H "Authorization: Bearer $TELVOX_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"from": "+918045670100",
"to": "+919876500199",
"url": "https://your.app/voice/answer",
"statusCallback": "https://your.app/voice/status",
"statusCallbackEvent": ["initiated"]
}'{
"call_sid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"to": "+919876500199",
"from": "+918045670100",
"status": "initiated",
"channel_id": "1719912345.1234"
}If from is not one of your active numbers you get 422 with an error message. A missing or invalid field returns 422 with message and errors. If the call cannot be placed you get 500 with {"error": "Failed to initiate call: ..."}. See errors.
What your url receives
After the callee answers, Connect POSTs this JSON to your url with the header X-CPaaS-Event: webhook.outbound. Reply with markup. For inbound calls the number's webhook URL gets {"from", "to", "direction": "inbound"} instead, with no call_sid, and has 5 seconds to answer.
{
"call_sid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"to": "+919876500199",
"from": "+918045670100",
"direction": "outbound"
}Fetch a call
Get one call by its SID. The record is updated while the call is live, so you can poll it, or use it to catch up on a status callback you missed. A call SID that is not on your account returns 404.
/api/calls/{callSid}Fetch one call's record by its SID.
curl https://connect.telvox.dev/api/calls/CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5 \
-H "Authorization: Bearer $TELVOX_API_KEY" \
-H "Accept: application/json"{
"call_sid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"from_number": "+918045670100",
"to_number": "+919876500199",
"direction": "outbound",
"status": "completed",
"duration": 47,
"price": null,
"price_unit": null,
"webhook_url": "https://your.app/voice/answer",
"status_callback": "https://your.app/voice/status",
"recording_url": null,
"recording_status": null,
"error_message": null,
"start_time": "2026-06-22T17:04:11.000000Z",
"ringing_at": "2026-06-22T17:04:13.000000Z",
"answered_at": "2026-06-22T17:04:19.000000Z",
"end_time": "2026-06-22T17:05:06.000000Z",
"created_at": "2026-06-22T17:04:11.000000Z",
"updated_at": "2026-06-22T17:05:06.000000Z"
}Other internal fields such as metadata may also appear. Do not depend on them.
Call history and live calls
There is no endpoint to list calls. Your call history is in the dashboard, and your status callbacks give you a record of every call as it happens. There is also no REST endpoint to redirect or hang up a live call. To pause, resume or stop recording on a live call, see Recordings.
Status callbacks
Connect POSTs JSON to your statusCallback as the call moves on. ringing, in-progress and the final status are always sent; initiated only if you list it in statusCallbackEvent. Field names follow Twilio's call progress events. Each request has a 5 second timeout and is not retried. Requests carry User-Agent: cPaaS-Platform/1.0, X-CPaaS-Event and X-CPaaS-Call-Sid headers.
| CallStatus | Sent when |
|---|---|
| initiated | The call is being placed. Only if you subscribed to it. |
| ringing | The destination starts ringing. |
| in-progress | The call was answered. |
| completed, busy, no-answer, failed, canceled | The call is over. WebhookEvent is call.hangup, IsFinal is true, and Duration and CallDuration are included. |
Callback payload
{
"ApiVersion": "2010-04-01",
"AccountSid": "AC3b1f...",
"CallSid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"CallStatus": "in-progress",
"Direction": "outbound-api",
"From": "+918045670100",
"To": "+919876500199",
"Caller": "+918045670100",
"Called": "+919876500199",
"CallerCountry": "IN",
"FromCountry": "IN",
"CallbackSource": "call-progress-events",
"SequenceNumber": "2",
"Timestamp": "Mon, 22 Jun 2026 17:04:19 +0000",
"WebhookEvent": "call.status",
"EventType": "call.status",
"CallbackId": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5:2",
"IsFinal": false,
"EventTimestamp": "2026-06-22T17:04:19.000Z",
"WebhookUrl": "https://your.app/voice/answer"
}| Field | Type | Description |
|---|---|---|
| CallSid | string | The call this callback is about. |
| CallStatus | enum | initiated, ringing, in-progress, completed, busy, no-answer, failed or canceled. |
| From, To | string | The calling and called numbers. Caller and Called carry the same values. |
| Direction | enum | outbound-api or inbound. |
| Timestamp | string | When the event happened, in RFC 2822 format. |
| EventTimestamp | string | The same moment in ISO 8601. |
| SequenceNumber | string | Goes up with each callback for the call. Use it to put callbacks in order. |
| WebhookEvent | enum | call.status for progress events, call.hangup for the final one. EventType carries the same value. |
| IsFinal | boolean | true on the last callback for the call. |
| CallbackId | string | CallSid:SequenceNumber. Use it to drop duplicates. |
| Duration | string | Final callback only. Billed minutes. |
| CallDuration | string | Final callback only. Call length in seconds. |
| HangupSource, HangupReason, HangupCause | string | Final callback only. Who ended the call and why. |
| ErrorMessage | string | Set when the call failed. |
| AccountSid, ApiVersion | string | Account identifier and a fixed API version string, kept for Twilio-style parsers. |
| CallerCountry, FromCountry | string | Country code of the calling number, for example IN. |
| RecordingSid, RecordingDuration | string | Present when the call was recorded. |
| ClientRef | string | The client reference set on the number in the dashboard, when there is one. |
| WebhookUrl | string | The markup URL used for this call. |
Fields with no value are left out. Other fields may appear; ignore the ones you do not use.
Securing your endpoint
Callbacks are not signed. Put a long random token in the callback URL and check it on every request. Match X-CPaaS-Call-Sidagainst calls you placed. Reply 2xx fast and do slow work later, and if you need to be sure of a call's final state, fetch it with GET /api/calls/{callSid}. More in webhooks & callbacks.