What a contact holds
| Field | Type | Description |
|---|---|---|
phone_numbers | string array (E.164) | Ordered list of numbers to dial. Order is dial priority: the first number is tried first, then the next on retry. |
data | JSON object | Arbitrary key/value data. Any CSV column you do not map to a structured field lands here, and the whole object is passed as custom_metadata on the call so your agent and prompts can reference it. |
external_id | string | Optional identifier from your own system. Used as the upsert key and is unique per campaign. |
timezone | string | IANA timezone (for example America/New_York) used for call-window scheduling. |
scheduled_at | datetime | ISO 8601 time before which the contact is not dialed. |
agent_id, agent_config, agent_number, agent_version, initial_context | overrides | Per-contact overrides of the campaign’s agent assignment, configuration, caller number, pinned version, and opening context. See Configurations for the campaign-level defaults these replace. |
Structured fields (
phone_numbers, external_id, timezone, scheduled_at, and the agent overrides) live as top-level columns. Everything else you send stays inside data. Do not duplicate a structured field inside data.Upload a CSV
In the campaign, open the contacts uploader and drag a CSV onto the drop zone (or click to browse). The uploader parses the file in the browser, auto-detects columns, and lets you adjust the mapping before sending.Drop the file
Drag a
.csv file onto the upload area. The file must be 10MB or smaller, and at most the first 1000 rows are read.Confirm the mapping
The uploader auto-detects phone columns by header name (for example
phone, phone_number, mobile, cell, tel) and maps Contact ID, External ID, Timezone, and Scheduled At when it recognizes their headers. Adjust any mapping that is wrong.agent_config and initial_context overrides are API-only — set them through the contacts endpoint.
Add contacts via the API
POST /campaign/campaigns/{campaign_id}/contacts upserts the batch. For each contact it matches an existing row by id first, then by external_id within the campaign, and updates it; otherwise it inserts a new contact.
| Limit | Value |
|---|---|
| Contacts per API request | 1000 |
| Contacts inline on campaign create or update | 5000 |
| CSV file size | 10MB |
| CSV rows read | 1000 |
Phone normalization
Numbers are normalized to E.164 using the campaign configuration’sdefault_country_code, so you can upload local-format numbers as long as they belong to that country. A number that cannot be parsed into a valid E.164 number fails the whole request with 422; the response lists the first invalid numbers.
Adding contacts to a campaign whose status is
closed returns 400. Reopen or recreate the campaign before uploading.Contact statuses
As a campaign runs, each contact moves through these statuses:| Status | Meaning |
|---|---|
pending | Eligible to be dialed and waiting in the queue. |
locked | Reserved by a worker and about to be dialed. |
in_progress | A call attempt is currently active. |
completed | The contact reached a terminal successful outcome. |
exhausted | All retry attempts were used without success. |
error | Processing failed in a way that stopped further attempts. |
Related
Configurations
Set the agent, caller number, country code, retries, and call window a campaign applies by default.
Results and analytics
Review attempts, outcomes, and assessment results per contact.

