Before you begin
- An outbound Voice agent with a script, voice, phone number, availability hours, and success criteria. Build and test it in the dashboard first; it’s easier than defining a full agent through the API.
- An API key (create one) and the agent’s ID. See the API quickstart if you haven’t made a request yet.
- A webhook endpoint. See Webhooks overview.
How outbound dialing works
Once an agent is active, Callers continuously picks eligible contacts and calls them:- Calls only go out inside the agent’s availability hours, in the agent’s time zone or the contact’s time zone if Use Contact’s Time Zone is on.
- Unanswered, busy, and voicemail attempts are retried according to the agent’s retry settings (total attempts, minimum wait between attempts, maximum attempts per day).
- Concurrency is capped by the agent’s Max Outgoing Calls in Parallel setting.
- Contacts on your Do Not Call list are skipped.
- Contacts you add while the agent is running are picked up automatically.
- When no eligible contacts remain, the agent stops on its own. Adding new contacts to a completed agent makes it eligible to run again; call
startif it’s stopped.
Step 1: Map your data to variables
Every key in a contact’sdata object becomes a variable your script can use as {{key}}. Before sending contacts, list the variables your script uses and make sure each contact provides them.
Variables marked required in the agent must be present. A single-contact request missing one fails with
400; in a bulk request the incomplete contact is skipped.
Step 2: Push contacts
Use the bulk endpoint for anything beyond a single contact. JSON bodies are limited to 1 MB, so split large lists into batches (a few hundred to a thousand contacts per request, depending on how muchdata each carries) and send them sequentially.
contactId next to your own record (or put your record ID in data, like crm_id above) so you can match webhook results later.
Re-sending a contact with the same phone number updates it instead of creating a duplicate, so retries are safe. See Manage contacts for the full behavior, multiple phone numbers per contact, and validation errors.
Step 3: Start and stop the agent
start on an active agent returns 400 Campaign already activated. and stop on a stopped agent returns 400 Campaign not activated.. Check active with GET /agents/{agentId} first, or treat those responses as success.
Stopping an agent pauses new calls. It doesn’t remove contacts or reset their attempts; starting again continues where it left off.
Step 4: Receive outcomes
Subscribe a webhook to the events you need. A typical outbound integration uses:
Subscribe through the dashboard or the API:
The API subscribe endpoint accepts the standard call events. To subscribe to
CONTACT_UNREACHED, CONTACT_LAST_DAILY_ATTEMPT, or events for custom success criteria, configure the webhook in the agent’s Webhooks settings in the dashboard.contact.id, contact.phoneNumber, or a value you put in contact.data. See Webhook events and Webhook payload.
Step 5: Track progress
Poll sparingly; webhooks are the primary signal. For dashboards and reconciliation:Response
contactsByStatuses counts calls instead (COMPLETED, IN_PROGRESS, ANALYZING, INTERESTED, NOT_INTERESTED, FAILED) and there is no totalContacts or answeredCallsCount.
List calls
GET /agents/{agentId}/calls returns the agent’s calls as a plain array, newest first, with page and perPage (default 50):
id, sessionId, contactId, agentId (and the legacy campaignId), status, hangUpReason, duration, createdAt, phoneFrom, phoneTo, messages, summary, goal, events, and recordingUrl when available. Keep paging until a page comes back with fewer than perPage items.
Download a call’s SIP trace
For calls placed over your own SIP trunk,GET /agents/{agentId}/calls/{callId}/pcap returns the packet capture of the SIP signaling (application/vnd.tcpdump.pcap). Share it with your carrier when you debug one-way audio or rejected calls. It’s only available for SIP calls and only for recent ones; otherwise the endpoint returns 404.
Step 6: Stop calling specific people
When someone converts or opts out in your system, archive them so the agent stops calling. Archived contacts are kept (with their history) and can be restored.Correct an outcome
If your team learns that a call’s outcome was wrong, set it from your system:callStatus accepts INTERESTED, NOT_INTERESTED, VOICE_MAIL, CALL_ME_LATER, DO_NOT_CALL, WRONG_NUMBER, or COMPLETED. Pass callReachedGoal with a success criterion’s name (for example, callReachedGoal=Demo%20Scheduled) to record which criterion was met.
Backfill missed webhooks
If your endpoint was down, you don’t have to guess what you missed. List the agent’s calls, then fetch the exact webhook payload for each one withGET /agents/{agentId}/calls/{callId}/webhook-payload and run it through the same handler your webhook uses. The payload matches the final delivery (its webhookEvent is the current state of the call), so skip calls you already have by id.
Node.js
Best practices
- One agent per use case. Keep script, schedule, and success criteria specific. Use agent groups to organize many agents.
- Push contacts ahead of the calling window so the agent can start dialing as soon as hours open.
- Store Callers IDs. Save
contactIdand the callidfrom webhooks to make reconciliation and re-fetching easy. - Backfill from the API. If your endpoint was down, list calls with
GET /agents/{agentId}/callsand fetch each payload withGET /agents/{agentId}/calls/{callId}/webhook-payload. See Backfill missed webhooks. - Test first. Run a few contacts (your own numbers) through the agent before loading a full list.
Troubleshooting
Bulk upload returns fewer added + modified than requested
Bulk upload returns fewer added + modified than requested
Some contacts were skipped: invalid or missing phone numbers, missing name, or missing required variables. Validate numbers before sending, or use
fixUSPhoneNumber=true for 10-digit US numbers without a country code.Bulk upload is rejected with 400 and a list of data issues
Bulk upload is rejected with 400 and a list of data issues
At least one contact has invalid
data (non-string value, invalid key, too large). Invalid data on any contact rejects the whole request; each issue includes the contact’s index in path.The agent stopped by itself
The agent stopped by itself
It ran out of eligible contacts. Add more contacts and call
start. Also check the agent in the dashboard for a failure reason (for example, a missing phone number).A contact was called again after I re-uploaded it
A contact was called again after I re-uploaded it
Re-uploading updates the existing contact and keeps its status. If it had been archived, re-uploading restores it (unless you send
"archived": true), and it can be called again if it still has attempts left.Related
Manage contacts
Validation, duplicates, filters, and archiving.
Scheduling and retries
Availability hours, retry rules, and concurrency.
Webhook events
Every event and when it fires.
Statuses and outcomes
What each call and contact status means.