Contact-list campaign
The Quickstart shows the minimal flow. This section covers the options and the failure cases.The contact-list endpoints below are part of the workspace API and need a scoped API key.
1. Create the campaign
POST /campaigns creates a campaign in DRAFT. Commonly used fields:
The full schema is on the endpoint’s reference page.
2. Attach phone numbers
POST /campaigns/{id}/phone-numbers with {"phoneNumberIds": [...]} (1–50 IDs). Use GET /phone-numbers to list
your numbers. Numbers already attached are skipped. A number that isn’t active in your workspace returns
400 phone_number_not_in_workspace.
3. Add contacts
POST /campaigns/{id}/contacts with up to 10,000 contacts per request:
phonemust be E.164 (+and 8–15 digits). It is not reformatted.metadataholds the values for the agent’s variables.- Duplicate phone numbers in the request are dropped (the first one wins). Numbers already in the campaign are not added again.
- If a phone number already exists elsewhere in your workspace, the existing contact’s name and metadata are kept, and the values you send are ignored.
- Contacts can only be added while the campaign is in
DRAFT(400 campaign_not_in_draftotherwise). - The response
{ "added": 2, "total": 2 }counts this request only:addedis the number of new contacts andtotalthe number of valid unique phones you sent.
POST /campaigns/uploads and pass the returned uploadId to POST /campaigns.
4. Start
POST /campaigns/{id}/start returns 204 when the campaign is running. It checks, in order:
5. Monitor
stats counts contacts by status: PENDING, IN_FLIGHT, MESSAGED, COMPLETED, FAILED and SKIPPED.
Calls are enriched after they end: the transcript, recording and extracted fields can appear a little later than the
call’s final status. See Recordings & transcripts.
6. Pause, resume, stop, restart
All four arePOST /campaigns/{id}/<action> with scope campaigns.edit and return 204.
Push leads with the public API
A standing API campaign stays running and calls each lead you push, then produces one final result per lead.1
Create and start an API campaign
POST /v1/campaigns (source defaults to API), attach numbers with POST /v1/campaigns/{id}/phone-numbers,
then POST /v1/campaigns/{id}/start. An API campaign can start with no contacts.2
Push each lead
Response (202)
3
Collect the result
Receive it by webhook, or poll
resultUrl (GET /v1/leads/{leadId}/result).Lead rules
idempotencyKeyis required (in the body, up to 200 characters). It identifies the lead within the campaign: pushing the same key again returns200with"deduplicated": trueand the originalleadId, and nobody is called twice. Use a stable ID from your system, such as your CRM’s lead ID. The same phone with a different key is a new lead.phoneis normalised. Include the country code; numbers without one are read as Indian numbers.fieldsare string values for the agent’s variables. Keys the agent doesn’t use are returned inwarnings.undeclaredVariablesand dropped.- A number on your Do Not Call list is accepted but not called; its result (
skipped) is produced immediately.
Every response includes a
requestId, also sent as the X-Request-Id header. Quote it when contacting support.
Push up to 100 leads at once
POST /v1/campaigns/{campaignId}/leads/batch (scope campaigns.edit) takes {"leads": [ ... ]} with 1–100
items. Each item is shaped exactly like the single-lead body, including its own idempotencyKey in the body.
The batch does not use the Idempotency-Key header.
200 with one result per item, in the order you sent them:
- Each item is
queued,duplicateorrejected, with the same rules as a single push. A rejected item carries the error code inerror; the other items are not affected. - Every result echoes the item’s
idempotencyKeyandphoneexactly as you sent them (when they were strings), for every outcome, including rejected and malformed items. Match results to your records with these rather than relying on order alone. - Items are processed in order, so a key that appears twice in the same batch is queued once and reported as
duplicatethe second time. - Items marked
"retryable": true(a429live cap or an error on our side) were not accepted. Send them again later with the sameidempotencyKey. - Problems with the campaign itself (
403,404,409) or a malformed envelope (422 invalid_body) reject the whole batch. - A batch counts as one request towards your rate limit.

