Skip to main content
A call is one outbound phone call, fired server-to-server. It is the primitive to reach for when your own system decides who to ring and when — a CRM trigger, a queue worker, a workflow step — and you don’t want to model it as a campaign. Each call is a durable record that owns its own retries, calling window and concurrency slot. Every dial it makes creates a session, so one call can have several sessions attached to it.
Three ways to start a voice conversation, and they are not interchangeable:
  • POST /calls — one outbound phone call, API key only, no human in the loop. This page.
  • POST /sessions — a web (browser) call for a signed-in user; requires a bearer login and returns a participant_token.
  • Campaigns — bulk outreach over a recipient list, with scheduling and per-recipient state.

Endpoints

POST /calls rejects bearer logins and agent-session tokens even though both can carry sessions:write. An agent-session token lives inside every running voice worker for the duration of a call, so accepting one would let the credential minted for a call originate the next one. Use a workspace API key.

Place a call

The agent must be published and channel=voice.

Request fields

The body is a strict allowlist — an unrecognised field is a 400 naming it, so a setting can never appear to take effect when it didn’t. custom_variables values must be strings, numbers, booleans or null: they are substituted verbatim into the prompt, so a nested object would render as raw data inside the agent’s instructions.

call_config

A retry re-checks the calling window, so a backoff can never push a dial outside it. A declined call (the callee actively rejected it) is never retried.

Fields that are refused

smart_formatter (extract_first_name, transliteration, transliteration_language) is accepted, stored and echoed back, but not applied to the call. It exists for contract compatibility with migrating integrations; don’t rely on it for behaviour.

Response

POST /calls answers 200 with the call record. Accepting a call is asynchronous — it may still be waiting for its calling window or queued behind your workspace’s concurrency cap — so there is no dispatch id to report yet. Poll GET /calls/{id} or subscribe to call_completed.
The response never echoes to_number — you supplied it, and repeating it would put a subscriber number into every orchestrator’s response logs. to_number_masked is enough to reconcile against your own record. There is no participant_token: this is a phone call, not a browser session.

Lifecycle

queued, scheduled, dialing and in_progress are active; completed, failed and cancelled are terminal. Cancelling a terminal call returns 409.

Inspect a call

GET /calls/{call_id} returns the record above plus one attempts entry per dial:
Each session_id is a full session — transcript, usage and recording all hang off it. outcome uses the same vocabulary everywhere in the platform: completed, no_media, no_answer, busy, declined, failed, user_disconnected, expired. sentiment is populated only for agents that opted into sentiment analysis. GET /calls lists them, filtered by status, agent_id, created_from and created_to, with standard offset pagination.

Idempotency

Send an idempotency_key and a repeat of the same request is refused with 409 naming the existing call, rather than dialling the number twice. This is the safe way to retry a POST /calls whose response you never saw.

Errors

The 402 and destination checks run before the call is queued, so no call record is created on refusal. Where a deployment restricts outbound destinations, a to_number outside the permitted prefixes is refused with 400 invalid_request (“to_number is not a permitted destination”).
Migrating an integration built against Ringg’s individual-call endpoint? /cu1/v1/calls and /cu1/v2/calls accept that request shape unchanged and translate onto this API — see Individual-call compatibility.

Outbound webhooks

Get call_completed pushed to you instead of polling.

Sessions

Transcript, usage and recording for each dial.