Adding Recipients
Who a campaign calls: upload a CSV to a one-shot campaign, or feed a continuous campaign over the REST API, with Copilot or MCP, or from Python with add_campaign_recipients. Covers recipient fields (phone, name, context, metadata, dedupeKey, isTest), E.164 validation, limits and de-duplication.
A recipient is one person a campaign calls. How you add recipients depends on the mode:
| Mode | How recipients are added |
|---|---|
| One-shot | Once, when the campaign is created: a CSV upload in the dashboard, or recipients on create_campaign. |
| Continuous | At any time while it's a draft, running or paused: REST API, Copilot or MCP, or Python. |
Recipients can't be removed one by one. To stop calling someone, skip them in a Pre-Call hook.
Recipient fields
| Field | What it's for | Limit |
|---|---|---|
phone | The number to call, in E.164 format with a leading +, such as +15551234567. Spaces, dots, dashes and brackets are removed; anything else makes it invalid. | Required |
name | Spoken in the greeting through {name}, and used as the customer's name in the help desk. | 500 characters |
context | Free text the AI reads before the call, such as "Ordered a blue sofa on 2 May, delivery pending". See what the AI knows. | 2,000 characters |
metadata | A JSON object for your own code: hooks and Python actions see it. The AI never sees it. | 4 KB as JSON |
dedupeKey | Your own ID for this call, such as an order number. See De-duplication. | 1-200 characters |
isTest | A test recipient: called normally, left out of the metrics. | true or false |
Use context for anything the AI should say or know, and metadata for IDs your hooks need, such as a customer or order ID.
Upload a CSV
On Create Campaign, choose One-shot, then upload a CSV file under Upload Recipients. Download the template gives you the format:
phone,name (optional),context (optional)
+15551234567,Jane,Ordered a blue sofa on 2 Mayphoneis required;nameandcontextare optional. Column names are case-insensitive, and the "(optional)" suffix may be left out.- Other columns are ignored. A CSV can't set
metadata,dedupeKeyorisTest. - If any row has an invalid phone number or a too-long value, the whole file is rejected with the number of bad rows. Fix them and upload again.
- Duplicate phone numbers are dropped.
- Up to 10,000 recipients and 10 MB per file. Split bigger lists across campaigns.
Feed a continuous campaign
REST API
curl -X POST "https://api.octocom.ai/rest/v1/campaigns/<campaignId>/recipients" \
-H "X-API-Key: <organization API key>" \
-H "Content-Type: application/json" \
-d '{"recipients": [{"phone": "+15551234567", "name": "Jane", "context": "Abandoned a cart with a blue sofa", "metadata": {"cartId": "c_123"}, "dedupeKey": "cart-c_123"}]}'The response is {"added": 1, "skippedDuplicates": 0}. Up to 10,000 recipients per request. The campaign's Configure page shows its exact endpoint and an example under Feed recipients via API. See the REST API for authentication.
The request returns once the recipients are queued. They're called as soon as the campaign is running, within its calling hours.
Python
From a recurring job, event handler or custom action, use add_campaign_recipients:
result = add_campaign_recipients(
"<campaignId>",
[{"phone": "+15551234567", "name": "Jane", "dedupeKey": "cart-c_123"}],
)
# {"added": 1, "skippedDuplicates": 0}A typical setup is a recurring job that finds customers to call, such as abandoned carts from the last hour, and adds them.
Copilot or MCP
Use append_campaign_recipients. It returns how many were requested, added and skipped as duplicates.
Errors
Adding recipients fails when the campaign is one-shot, completed or canceled, or when a phone number isn't valid E.164.
De-duplication
- Within one request, only the first recipient per phone number and per
dedupeKeyis kept. - A phone number already waiting or being called in the campaign is skipped.
- A phone number whose earlier call is finished can be added again. A continuous campaign can call the same person again later.
- A
dedupeKeyis used once per campaign, forever, even after its call finished. Use it to make sure the same event never causes two calls, for examplecart-<cartId>. To call someone again for a new reason, use a new key.
Skipped recipients are counted in skippedDuplicates. Different campaigns don't de-duplicate against each other.