TelVox · Connect · Quickstart
Make your first call
This quickstart places a real outbound call and follows it from start to finish: create an API key, claim a number, POST to /api/calls with a url, reply with XML from that url, and handle the status callback as the call moves through ringing, in-progress and completed. Samples are shown in cURL, Node and Python.
Connect is live. We set up your account with you. After that you claim numbers and create API keys yourself in the dashboard. The API is v0.x, so field names may change before v1.
Before you start
You'll need:
- An account: request access on the signup page and we'll set it up with you.
- A public HTTPS URL for your
urlandstatusCallback(a tunnel to your laptop works while testing). - Any HTTP client. There are no TelVox SDKs yet, so the samples use plain cURL, Node
fetchand Pythonrequests.
1 · Create an API key
An API key authenticates every request. Create one in the dashboard under API keys. It starts with sk_live_ and is shown once, so copy it then. TelVox keeps only a one-way hash, so nobody can show it to you again. Keep it on your server and send it as a Bearer token in the Authorization header. See Authentication & API keys for the full model.
export TELVOX_API_KEY="sk_live_…" # created in the dashboard, shown once2 · Claim a number
Calls can only be placed from a number you have claimed. In the dashboard, open Numbers and choose Claim a number. The number you claim is the from in the next step. If from is not one of your active numbers, the API answers 422 and the call is not placed. There is no API to search for or buy numbers. See Phone numbers for the settings on each number, including the webhook URL for inbound calls.
3 · Place a call
POST to /api/calls with from, to and url. Add statusCallback to follow the call, and list initiated in statusCallbackEvent if you want that event too. Send Accept: application/json so any error comes back as JSON. Your url has to be reachable before you run this; step 4 shows what it returns.
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"]
}'TelVox responds with 201 and the new call. The call_sid (CA plus 32 characters) identifies the call in the API and in every callback. The numbers above are placeholders; use your claimed number and a real destination.
{
"call_sid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"to": "+919876500199",
"from": "+918045670100",
"status": "initiated",
"channel_id": "1719912345.1234"
}4 · Reply with XML from your url
When the callee answers, TelVox POSTs JSON call details to your url. Your app replies with TwiML-style XML, and TelVox runs the verbs in order: here <Say> greets the callee and <Gather> waits for one keypress.
POST /voice/answer HTTP/1.1
Content-Type: application/json
User-Agent: cPaaS-Platform/1.0
X-CPaaS-Event: webhook.outbound
X-CPaaS-Call-Sid: CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5
{
"call_sid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"to": "+919876500199",
"from": "+918045670100",
"direction": "outbound"
}<Response>
<Say>Thanks for calling.</Say>
<Gather action="https://your.app/voice/menu" numDigits="1" timeout="5">
<Say>Press 1 for sales, 2 for support.</Say>
</Gather>
</Response>When the caller presses a key, TelVox POSTs {"digit":"1"} to the Gather action URL (an empty digit on timeout), and your app returns the next XML. There is also a smaller JSON form, {"response":[{"say":"Hello"},{"hangup":true}]}, which covers say, play, dial, wait, hold, enqueue and hangup but not gather or record. See Call-control markup for every verb.
5 · Handle status callbacks
As the call advances, TelVox POSTs JSON to your statusCallback: ringing, in-progress, then a final status (completed, busy, no-answer, failed or canceled). initiated comes only if you listed it in statusCallbackEvent. Field names follow Twilio's. The last callback has IsFinal set to true and adds CallDuration in seconds.
Each callback is sent once, with a 5 second timeout, and is not retried. Reply with a 2xx quickly and do slow work later. Callbacks are not signed, so put a long random token in your callback URL and check it, and match X-CPaaS-Call-Sid against calls you placed.
POST /voice/status HTTP/1.1
Content-Type: application/json
User-Agent: cPaaS-Platform/1.0
X-CPaaS-Event: call.status
X-CPaaS-Call-Sid: CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5
{
"ApiVersion": "2010-04-01",
"CallSid": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5",
"CallStatus": "in-progress",
"Direction": "outbound-api",
"From": "+918045670100",
"To": "+919876500199",
"Caller": "+918045670100",
"Called": "+919876500199",
"CallbackSource": "call-progress-events",
"SequenceNumber": "2",
"Timestamp": "Mon, 22 Jun 2026 17:04:19 +0000",
"EventTimestamp": "2026-06-22T17:04:19.000Z",
"WebhookEvent": "call.status",
"EventType": "call.status",
"CallbackId": "CA7Q2M9X4K1B8N3T6R0W5Y2Z9C1V4H7JL5:2",
"IsFinal": false
}See Webhooks & callbacks for every field in the callback. If you miss one, fetch the call with GET /api/calls/{callSid}.
Record the call
To record, add the record attribute to <Dial> (for a bridged call), or use the <Record> verb (for example for voicemail). When the file is ready, TelVox POSTs a recording status callback with a RecordingUrl. Fetch the WAV from GET /api/recordings/{name} with your API key.
<Response>
<Say>This call is recorded.</Say>
<Dial record="record-from-answer"
recordingStatusCallback="https://your.app/voice/rec-status">
<Number>+919876500111</Number>
</Dial>
</Response>What's next
- Build a menu: chain Say, Gather and Dial into a keypress IVR.
- Recordings: pause, resume or stop recording on a live call, and download the file.
- Status callbacks: follow each call's lifecycle.
- Calls reference: every parameter, status and field on a call.