Dialer API
The Dialer API runs outbound calling campaigns: preview, progressive, and predictive modes for human agents, plus agentless voice broadcast and AI-voice-agent dispatch. You create a campaign, attach contact lists, then either let the pacing engine dial (progressive / predictive) or pull the next contact yourself (preview). Live statistics, local-presence caller-ID coverage, and per-attempt dispositions round out the surface. Base path:/api/v1/dialer
Authentication: API key (X-API-Key: dv_live_sk_…) or a Clerk session (Authorization: Bearer <token>).
Scopes: read endpoints require dialer:read; create / update / delete endpoints require dialer:write.
Response envelope: every endpoint returns { "data": …, "meta": { "request_id", "timestamp" } }. Errors return { "error": { "code", "message" } } (validation failures also carry error.issues).
Request samples: the first ten operations below ship the six-language sample group (cURL, Node.js, Python, Go, Ruby, PHP — every operation up to POST /campaigns/{id}/dispositions); every later operation, and every read endpoint that only returns JSON, ships the cURL + TypeScript pair the sample-writing policy guarantees. Hero flows past the cut keep the full group inside Worked sequences below.
Outbound calls placed by the dialer egress only through Orbit’s wholesale voice path. The caller ID you set on a campaign must be a number your account owns (or one you’ve verified) — a foreign number is rejected with
422 UNVERIFIED_CALLER_ID on both create and update.Using the SDKs
The Node SDK is the only client with typed dialer helpers today (the SDK index documents every other client as core-scope). Pick the preview claim withorbit.dialer.getNextCall() and dial it with orbit.dialer.dial() — worked call sequences for Python and Go (documented client.request / client.Request escape hatch) come later on this page under SDK call flows.
ApiResponse envelope. See the SDK index at SDK quickstart.
Campaign modes
dispatch_mode is a second axis. The default human fronts the call with a live operator. voice_agent connects each answered leg to the AI voice agent named by voice_agent_id; those campaigns are paced by POST /campaigns/:id/batch-dispatch instead of the human-agent scheduler.
Create a campaign
POST /api/v1/dialer/campaignsdialer:write. Created in draft status — activate it later with PATCH /campaigns/:id.
string
required
Human-readable campaign name. 1–200 characters.
string
required
One of
preview, progressive, predictive, agentless.string
required
Caller ID presented on each dial, in E.164 format. Must be a number your account owns.
number
default:"1.2"
Predictive over-dial ratio. 0.8–3.0.
number
default:"0.03"
Rolling abandon-rate ceiling. Greater than 0 and at most
0.03 (the FCC 3% cap). See FCC abandon-rate ceiling.number
default:"20"
Predictive pacing target — seconds to answer. 5–120.
string
default:"human"
human or voice_agent. voice_agent requires voice_agent_id.string
AI voice agent that answers each leg when
dispatch_mode='voice_agent'. Must reference an existing, non-archived voice agent in your workspace.number
default:"10"
Maximum simultaneous in-flight legs a single batch-dispatch invocation may originate. 1–200.
array
default:"[]"
Skills an agent must hold to take this campaign’s calls. Accepts a string array (
["billing","french"]) or per-skill objects ([{ "skill": "billing", "min_level": 3, "weight": 2 }]).array
default:"[]"
Rotation pool of E.164 numbers for local-presence dialing. Every entry must be a number your account owns. Required (non-empty) when
caller_id_strategy is not single.string
default:"single"
single (always caller_id_e164), npa_match (present the pool number matching the lead’s area code), or round_robin.string
HTTPS audio URL played on answer in
agentless mode. Required for agentless; rejected for every other mode.string
HTTPS audio URL dropped when answering-machine detection fires on an agent-driven dial. Not valid for
agentless mode.number
Per-campaign preview countdown the agent has to accept a contact. Only valid for
preview mode; omit to use the platform default.boolean
default:"false"
Record calls on this campaign. Requires
recording_consent_acknowledged: true in the same request.boolean
Affirms you have authority to record and the two-party-consent disclaimer will play. Required when
recording_enabled is true. Internal API-key callers cannot acknowledge on behalf of an operator (401).boolean
default:"false"
Request carrier answering-machine detection on each dial.
string
Voice application (UUID) bound to answered legs.
object
default:"{}"
Per-outcome retry backoff keyed by outcome bucket (
no_answer, busy, machine, machine_silence, failed). Each entry sets retry delays (60–86400s) and max_attempts (0–10). Empty {} keeps the platform’s uniform retry schedule.object
Human-agent guided-dialog script (ordered sections plus per-disposition branches). Omit to store the canonical empty script
{ "version": 1, "sections": [], "branches": {} }.object
default:"{}"
Arbitrary key-value pairs stored on the campaign.
max_abandon_rate is capped at 0.03 (3%) per the FCC Telemarketing Sales Rule — a create that sends a higher value fails validation with 422 VALIDATION_ERROR. See FCC abandon-rate ceiling.List campaigns
GET /api/v1/dialer/campaignsdialer:read. Cursor-paginated, newest first. Soft-deleted campaigns are excluded.
Query parameters
Each row carries a bounded
contacts_total / contacts_connected rollup. When a campaign’s contact count exceeds the scan cap, contacts_count_capped is true and the totals are a floor; the envelope’s contacts_count_capped_at reports where the count stops being exact.
On a workspace that has never created a campaign — or one provisioned before the dialer shipped — this endpoint returns an empty list (
items: []) rather than an error, so an empty-state UI renders cleanly.Campaign stats summary
GET /api/v1/dialer/campaigns/statsdialer:read. Totals across every non-deleted campaign — the source of truth for a dashboard KPI strip, independent of pagination.
Query parameters
avg_abandon_rate is null when no active campaign carries a 30-day rate.
Get a campaign
GET /api/v1/dialer/campaigns/{id}dialer:read. Returns the full campaign row; campaign_script is always the canonical shape.
Update a campaign
PATCH /api/v1/dialer/campaigns/{id}dialer:write. Partial update — only the fields you send change. Accepts the same fields as create, plus status.
string
active, paused, completed, or aborted. aborted is the operator-initiated terminal state — once aborted, a campaign cannot be re-activated. Transitioning to active is blocked while your workspace’s compliance emergency-stop is engaged.voice_agent_id, voicemail_message_url, preview_decision_seconds, and call_script_id accept null to clear them. Sending caller_id_e164 or any caller_id_pool entry re-runs the ownership check. Sending retry_policy or campaign_script replaces the whole object.
Soft-delete a campaign
DELETE /api/v1/dialer/campaigns/{id}dialer:write. Soft-deletes the campaign and flips its pending contacts to archived. Terminal contacts (connected / failed / dnc / exhausted / wrong_number) are preserved untouched as a compliance audit trail. Campaign, lists, and contacts commit in one transaction.
List contact lists
GET /api/v1/dialer/campaigns/{id}/listsdialer:read. Cursor-paginated. Each list carries total_contacts and a live contacts_dialing badge.
Query parameters
Upload a contact list
POST /api/v1/dialer/campaigns/{id}/listsdialer:write. Attaches a new contact list to the campaign and enqueues its contacts.
string
required
List name. 1–200 characters.
array
required
Contacts to dial. At least one. Each entry:
phone_e164 (required, E.164), display_name (optional, ≤200 chars), contact_id (optional, links to a CRM contact). Duplicate phone_e164 values within the upload are de-duplicated (first occurrence wins) and reported back.number
default:"5"
Maximum dial attempts before a contact is exhausted. 1–20.
Soft-delete a contact list
DELETE /api/v1/dialer/campaigns/{id}/lists/{listId}dialer:write. Soft-deletes one list within the campaign. Pending contacts in the list flip to archived; terminal contacts are preserved as audit trail.
Campaign live stats
GET /api/v1/dialer/campaigns/{id}/statsdialer:read. Wallboard detail for one campaign: per-status contact breakdown, a 24-hour call-outcome rollup with average handle time, and a rolling 30-minute abandonment rate that mirrors what the pacing engine throttles against.
avg_handle_time_seconds is null until a connected call has terminated. contacts_count_capped is true when the contact set exceeds the scan cap (the totals are then a floor).
Errors
Caller-ID coverage
GET /api/v1/dialer/campaigns/{id}/caller-id-coveragedialer:read. Local-presence analytics for one campaign: which destination area codes (NPAs) your caller-ID pool can present locally, the rolling answer rate per destination NPA, and the highest-volume under-covered NPAs to add a local number for. Read-only; originates no calls. North American Numbering Plan (+1) numbers only.
Voice-agent campaign rollup
GET /api/v1/dialer/voice-agents/{agentId}/campaignsdialer:read. Lists the dispatch_mode='voice_agent' campaigns bound to one AI voice agent, with a per-campaign contact and call-outcome rollup over a rolling window. Cursor-paginated (limit / cursor, next_cursor / has_more). Human-mode campaigns never appear here. The envelope discloses contacts_count_capped_at and rollup_window_days. Returns a zeroed rollup (campaign_count: 0) for an unprovisioned workspace rather than an error.
Submit a disposition
POST /api/v1/dialer/campaigns/{id}/dispositionsdialer:write. An agent submits the outcome of a dial attempt; the platform routes the contact’s lifecycle status from the disposition (for example do_not_call adds the number to your do-not-call list, callback_later reschedules the contact).
string
required
The
attempt_id returned when the dial leg was placed (from POST /dial or the batch-dispatch legs payload).string
required
Agent-selected disposition string (1–100 chars). The routing matrix matches it case-insensitively — see Dialer Dispositions.
string
ISO-8601 timestamp. Required when the disposition reschedules a callback (
callback_later / callback). Must be in the future and at most 90 days ahead.string
Free-form agent notes, up to 2000 characters.
Get the next contact (preview mode)
GET /api/v1/dialer/next-calldialer:read. For preview-mode campaigns: atomically claims the next eligible contact so two agents polling at once never receive the same row. Progressive / predictive campaigns return 204 No Content — the pacing engine dials those instead. A preview contact stuck dialing for more than five minutes (the agent abandoned the tab) becomes re-claimable.
The claim + dial round trip is worked end to end in every supported language under SDK call flows below.
cURL
progressive or predictive (the pacing engine handles dialing).
Errors
Place a preview dial
POST /api/v1/dialer/dialdialer:write. Originates the dial leg for a contact the agent claimed via GET /next-call. Only valid for active preview campaigns; progressive / predictive campaigns dial through the pacing engine. Requires an authenticated operator — an API-key caller cannot dial on behalf of an agent (401 AGENT_REQUIRED).
The claim + dial round trip is worked end to end in every supported language under SDK call flows below.
string
required
The preview campaign to dial within.
string
required
The contact (from
GET /next-call) to dial.cURL
attempt_id — it’s what the agent submits back on POST /campaigns/:id/dispositions once the call ends.
Errors
Batch-dispatch voice-agent legs
POST /api/v1/dialer/campaigns/{id}/batch-dispatchdialer:write. Paces a dispatch_mode='voice_agent' campaign: originates up to the campaign’s concurrency_limit simultaneous AI-voice-agent legs. Each answered leg connects to the campaign’s voice_agent_id.
number
Optional per-invocation cap on legs to originate. 1–200. The effective cap is the smaller of this and the campaign’s
concurrency_limit.- 202 Accepted — every claimed leg was originated.
- 207 Multi-Status — some legs originated and some failed. Inspect each
legs[].okfor the per-leg result. - 204 No Content — no eligible contacts were pending, so nothing was dispatched on this tick.
Worked sequences
The trim above cuts every operation after the fifteenth down to the cURL + TypeScript pair. The hero flows past that cut keep the full six-language group here. The preview-dial chain — claim one contact withGET /next-call, originate its leg with POST /dial, then submit the outcome with POST /campaigns/:id/dispositions — is the canonical dialer loop: poll a claim, bridge it, record what happened, repeat. It is the whole operator turn of a preview campaign in three calls. Each step below carries the previous step’s response forward: data.contact.id from the claim feeds the dial, and the dial’s attempt_id feeds the disposition, so one claimed contact is dialed and closed exactly once.
1. Claim the next contact
"id": "dlc_xyz" inside data.contact (see Get the next contact (preview mode) for the full envelope). A 204 No Content means the campaign is not in preview mode and the pacing engine dials instead — there is nothing to claim.
2. Place the preview dial
POST /dial takes an operator session (Authorization: Bearer <token>; an API key is refused with 401 AGENT_REQUIRED), and contact_id is the claim’s data.contact.id from step 1.
202 Accepted; the envelope carries data.attempt_id ("datt_abc123") plus the contact_id you dialed (full envelope under Place a preview dial). Hold the attempt_id for step 3.
3. Submit the disposition
attempt_id is data.attempt_id from step 2 ("datt_abc123"); the disposition string routes the contact’s lifecycle per the Dialer Dispositions matrix.
callback_later disposition requires a future callback_scheduled_at within 90 days (422 VALIDATION_ERROR otherwise); do_not_call writes the number to your do-not-call list. The full matrix lives on Dialer Dispositions.
Voice-agent batch dispatch
The second hero flow paces adispatch_mode='voice_agent' campaign instead of the human loop: one scheduler tick calls POST /campaigns/:id/batch-dispatch and the platform originates up to concurrency_limit AI-voice-agent legs. Pin one Idempotency-Key per tick below, so a retried tick replays the original dispatch instead of originating a second batch of legs; retry it unchanged if the call times out.
202 Accepted when every claimed leg originated, 207 Multi-Status when some failed (inspect legs[].ok), or 204 No Content when no contact was pending; the worked envelope is under Batch-dispatch voice-agent legs above.
SDK call flows
The full preview round trip — claim the next contact withGET /next-call, dial it with POST /dial — in every supported language. Only the Node SDK has typed dialer helpers today — the Python SDK and Go SDK are core-scope, so for the dialer you drop to the documented escape hatch: client.request(method, path, ...) in Python and client.Request(ctx, method, path, ...) in Go. The voice-agent batch-dispatch call follows the same pattern and is worked in full under Batch-dispatch voice-agent legs above; the disposition call follows it too. All languages carry the same auth, retry, and Idempotency-Key behavior as the typed resource.
FCC abandon-rate ceiling
max_abandon_rate is capped at 0.03 (3%) per the FCC Telemarketing Sales Rule. Any create or update that sends a higher value is rejected with 422 VALIDATION_ERROR. The pacing engine also auto-flips a campaign to status: 'aborted' if its rolling 30-day abandon rate trips the ceiling. aborted is terminal and cannot be re-activated — review the abort reason and create a new campaign.
See also
- Dialer Dispositions — the full disposition matrix and contact-status enum
- Validate caller-ID coverage before launching a campaign — pre-launch coverage audit with worked envelope and SDK reads
- Voice API — calls, SIP trunks, IVR, conferences
- Numbers API — buy and manage the DIDs you present as caller ID