Outbound calls
Placing a call, and reading what happened.
curl "$TONE_API/v1/calls" -X POST \
-H "Authorization: Bearer $TONE_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'content-type: application/json' \
-d "{\"agentId\":\"$AGENT\",\"numberId\":\"$NUMBER\",\"toE164\":\"+919876543210\"}"agentId is required on a number that routes to a Tone agent and
forbidden on a BYO number — the number decides which, and either mistake is a
422.
What happens before the carrier is called
The compliance gate runs inside the same transaction as the call
record. So a refusal is a 403 with the audit rows already written and no
carrier contacted — the block and its evidence are the same event.
One thing runs before the gate: your concurrency quota. Over it, you get
429 concurrent_call_limit_reached with no verdict recorded, because no dial was
attempted. That is a different failure from a rate limit and needs a different
response — see Limits.
Lifecycle
status | |
|---|---|
queued | Accepted; the carrier has not connected it yet |
in_progress | Connected |
ended | Finished. disposition now says how |
disposition | |
|---|---|
answered | A person picked up |
no_answer | Rang out |
busy | Engaged |
voicemail | An answering machine took it |
failed | The carrier could not complete it |
unknown | We genuinely do not know |
Treat both as open sets — new values can appear and are not a breaking change.
Two honesty rules worth knowing: a call abandoned in queued reads as ended
after 15 minutes, and one stuck in_progress past 65 minutes reads as ended with
unknown and bills nothing. We would rather report "we do not know" than
invent an outcome or charge for one.
When the carrier's lines are all busy
Every carrier account carries a fixed number of calls at once, shared by
everyone dialling through it. When they are all in use, your dial is still
accepted: you get 201 with status: queued, and Tone places the call the
moment a line frees, in the order calls arrived. A call that waits longer than
10 minutes is not placed. It ends with disposition: failed and
endReason: carrier_capacity, and you get the usual call.completed and
call.failed events.
A promotional call whose calling window closes while it waits ends with
endReason: calling_window_closed instead of being placed late.
Reading it back
Poll if you must, but the call log is not where you should learn a call ended:
curl "$TONE_API/v1/calls/$CALL" -H "Authorization: Bearer $TONE_KEY"Subscribe to call.completed instead. See Webhooks.
To find every call one campaign placed — retries included — filter the call log rather than reading the recipient list, which names only each recipient's last attempt:
curl -H "Authorization: Bearer $TONE_KEY" \
"$TONE_API/v1/calls?campaign_id=$CAMPAIGN&limit=100"Test mode
The magic numbers produce fixed outcomes, so CI can assert on them, and nothing rings. See Test mode.