Calls
Outbound calls
You place an outbound call with one request; Voxa queues it, dials it when a call slot is free and the agent's calling hours are open, and records everything that happens.
Place a call
POST /api/v1/agents/{agent_id}/call needs the calls.place permission.
curl -X POST https://voxa.abhinavyadav.in/api/v1/agents/$AGENT_ID/call \
-H "X-API-Key: $VOXA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+919812345678",
"from_number": "+918035001234",
"user_data": {"customer_name": "Rohan", "slot": "11 am", "order_id": "ORD-5531"},
"ignore_call_window": false
}'
201 Created:
{"call_id": "00b1d622a8f04f4f8f3a3c2d9e5b7a11", "status": "queued", "position": 3}
position is the call’s place in your workspace’s queue, counting itself: 1 means no other call is waiting ahead of it.
Request body
| Field | Type | Default | Allowed | What it does |
|---|---|---|---|---|
to | string | required | 8 to 15 digits, optional leading + | The number to call. Voxa adds the + if you leave it out, so include the country code (919812345678 for India). |
from_number | string | "" | same format, or empty | The caller ID. Falls back to the agent’s phone_number. |
user_data | object | {} | any JSON object | Fills the agent’s {variables} and is sent to your tools. |
ignore_call_window | boolean | false | Dial even outside the agent’s calling hours. |
Caller ID
The call is placed from from_number if you send it, otherwise from the agent’s phone_number. If neither is set, you get 400 no caller number: give this agent a Plivo number, or pass from_number.
Use a number assigned to your workspace (see Phone numbers in the console, or GET /api/v1/numbers).
User data
user_data is a free-form JSON object saved on the call. Voxa uses it in three places:
- Variables. Each key fills the matching
{variable}in the welcome message and prompt. See Variables. - Tools. It is sent to your tools as
_call.user_datain everyPOSTtool request. See Tools. - The call record. It comes back as
user_dataon the call and in webhooks, so you can match the call to your own records.
Keys the agent doesn’t use are kept anyway. Put your own ids (order_id, customer_id) here.
{caller_phone} is filled automatically with to; you don’t need to send it.
What happens next
- The call is saved as
queuedand acall.queuedevent is sent with itsposition. If it is outside the calling window, the event message says it is waiting for it. - The worker takes the oldest queued calls while your workspace has free call slots. See The queue.
- When a slot is free (and credits and calling hours allow), the call moves to
ringingand acall.dequeuedevent records how long it waited. Then Voxa asks Plivo to dial, and sendscall.placed. - When the person answers, the call becomes
in-progressandcall.startedis sent. - If nobody answers, the call ends as
no-answer,busy,failedorcanceled, with Plivo’s hang-up cause inhangup_reason.
Plivo is told to drop the call 30 seconds after the agent’s max_duration_seconds, as a safety net behind Voxa’s own limit.
Cancel or hang up
POST /api/v1/calls/{id}/hangup cancels a queued call or ends a live one. It needs calls.place.
curl -X POST https://voxa.abhinavyadav.in/api/v1/calls/$CALL_ID/hangup -H "X-API-Key: $VOXA_API_KEY"
{"ended": true}
| Call status | Result |
|---|---|
queued | canceled, with hangup_by: "api" and hangup_reason: "canceled from the console". Never dialled, never charged. |
ringing | The record is closed as completed, with hangup_by: "api", and is not charged. The phone may keep ringing briefly; if the person answers, Voxa hangs up straight away. |
in-progress | The agent stops at once and the call ends as completed, with hangup_by: "api" and hangup_reason: "ended from the console". It is charged for the time it ran. |
| Already ended | Nothing changes. |
Placing many calls
To call a list of contacts, upload it as a batch: Voxa schedules the calls, retries the people it didn’t reach and gives you the results as a CSV. Or send one request per number. Every call goes into the same queue, so you can send them all at once and Voxa dials them as slots free up, oldest first.
- Mind the rate limit: 120 requests per minute per key by default.
- Each request checks your balance; when it reaches ₹0, new requests get
402, and calls still queued fail without of creditswhen their turn comes. - Queued calls expire after 24 hours.
Errors
| Status | detail | Fix |
|---|---|---|
400 | no caller number: ... | Pass from_number or give the agent a phone_number. |
402 | out of credits: add credits in Billing | Top up in the console. |
403 | workspace not active | Your workspace isn’t active yet (or was suspended). |
404 | agent not found | Check the agent id. |
422 | Validation errors, or Plivo is not available to this workspace | Check to and from_number; ask the Voxa team about provider access. |
Find your calls
curl "https://voxa.abhinavyadav.in/api/v1/calls?direction=outbound&status=queued,ringing,in-progress" \
-H "X-API-Key: $VOXA_API_KEY"
See The call object for every filter.