Skip to main content
POST /v1/calls dials a phone number and connects whoever answers to an agent — the dashboard’s test on phone, for your code. the bot is started and the greeting synthesised while the phone rings, so the callee hears it the moment they pick up.

request

string
required
the agent — its UUID or ua_… handle. it must be deployed and use a voice that can take calls (muga or mulberry). a draft answers 409 agent_not_deployed: a call never runs a configuration you did not release.
string
required
the number to dial, in E.164: + and 7–15 digits. spaces are tolerated.
string
the caller id, as one of your own numbers: a SIP trunk’s number, or a rented number that is active and connected to this agent.
string
dial from this SIP trunk, by id. wins over fromNumber.
object
call behaviour for this one call. omit to use the agent’s stored settings.
curl

response

returns 202 Accepted as soon as the carrier takes the dial.
string
the call’s id — what GET /v1/calls/{callId} and the dashboard’s conversations show. store it to correlate the call with your own records.
string
always calling here. the call’s real state is on GET /v1/calls/{callId}.
string
the agent’s UUID.
string
the number dialed, normalised.
string | null
the caller id the callee sees. null when the platform’s shared number was used.

which number the call comes from

a rented number dials out only for the agent it is connected to. connect it first with POST /v1/phone-numbers/{id}/agent; otherwise you get 409 number_not_connected. the dashboard does that step for you; the api keeps it explicit so an inbound route is never moved as a side effect of placing a call.

following the call

poll GET /v1/calls/{callId} — or list them all with GET /v1/calls.
curl
string
starting (dialing, bot launching) → running (answered) → completed. failed if it never connected or errored; expired if no end signal ever arrived (never charged).
string
phone for calls placed here and for inbound calls on your numbers; web for web calls; realtime for the realtime socket.
object[] | null
every turn, once the call has ended. offsetSeconds is where the turn starts in the recording.
string | null
a signed URL valid for a few minutes. fetch it when you need it; do not store it.
string | null
customer_ended, never_connected, … — why the call ended.

call history

GET /v1/calls lists every billed call on the account, newest first, in the same shape. page with limit (1–100, default 25) and before = the last id you have; nextCursor is null on the last page. each call names the agent that ran it in agentSlug, so filter on that for one agent’s history.

billing and limits

a phone call is billed like a web call — per second on pay-as-you-go, on a slot on a concurrency plan — plus the carrier’s per-minute cost when it goes out over a rented number. it takes one concurrent slot for its whole duration (limits), and each account has a daily cap on outbound calls (429 agent_phone_daily_limit).

errors

no such agent, or fromNumber / fromTrunkId is not one of your numbers.
the agent was saved but never deployed, so no version is live to run the call. deploy it with POST /v1/agents/{agent_ref}/deploy; nothing is started or billed until you do.
the rented number in fromNumber is not connected to this agent, or its rental is not active.
the agent’s voice engine cannot take phone calls. switch it to muga or mulberry.
the account cannot fund a usable call. top up, or enable auto top-up.
every concurrent slot is busy, or today’s outbound cap is reached.
the carrier could not place the call. nothing was charged; retry.
for a browser call use POST /v1/webcall; for your own audio pipeline, the realtime socket. all three land in the same call history.
next: phone numbers.