curl -X POST https://api.60db.ai/agents/campaigns \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: spring-renewals-2026-11-12" \
-d '{
"name": "Spring renewals",
"agent_id": "AGENT_ID",
"phone_line_id": "PHONE_LINE_ID",
"timezone": "Asia/Kolkata",
"start": "now",
"calling_windows": [{ "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "20:00" }],
"max_attempts": 3,
"retry_delay_secs": 1800,
"max_consecutive_failures": 10,
"variable_mapping": { "customer_name": "name" },
"csv_text": "phone_number,name\n+919876543210,Asha\n+919812345678,Ravi\n"
}'
Agent Campaigns
Create Campaign
Create an outbound campaign from a recipient CSV. Calls start now or at a set time
POST
/
agents
/
campaigns
curl -X POST https://api.60db.ai/agents/campaigns \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: spring-renewals-2026-11-12" \
-d '{
"name": "Spring renewals",
"agent_id": "AGENT_ID",
"phone_line_id": "PHONE_LINE_ID",
"timezone": "Asia/Kolkata",
"start": "now",
"calling_windows": [{ "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "20:00" }],
"max_attempts": 3,
"retry_delay_secs": 1800,
"max_consecutive_failures": 10,
"variable_mapping": { "customer_name": "name" },
"csv_text": "phone_number,name\n+919876543210,Asha\n+919812345678,Ravi\n"
}'
Create a campaign. The CSV is parsed and stored as recipients in this request. Invalid numbers and do-not-call numbers are stored as
skipped recipients. Duplicate numbers are dropped. A campaign with start: "now" starts dialing on the next scheduler tick. A future start makes it scheduled.
Check the file first with Preview campaign recipients. For the rules, see Outbound campaigns.
Request
Headers
1 to 128 printable characters with no spaces. Send the same key and the same body to retry safely. The retry gets the first campaign back with
200. The same key with a different body returns 409 IDEMPOTENCY_CONFLICTBody
"now", or an ISO 8601 time with an offset or Z that is in the future, for example 2026-12-01T09:00:00+05:30. A time without an offset is refused1 to 20 windows. Each is
{ "days": [1, ...], "start": "HH:MM", "end": "HH:MM" }. days are ISO weekdays: 1 is Monday, 7 is Sunday. end may be 24:00. A window cannot cross midnight. Default: every day, 09:00 to 20:00, in timezone. Nothing dials outside a windowHow many calls run at once. On a 60db Dialer line this is always 3, and any value you send is ignored. On your own line (
byo), send 1 to 50. Default 3. The workspace limit also applies. See Concurrency0 to 604800 (7 days). Wait before the next attempt. A retry that falls outside the calling windows waits for the next window to open
1 to 1000, or
null for no limit. The campaign pauses after this many calls in a row that did not connectThe recipient CSV as text. Up to 5 MB and 20,000 rows. It needs a phone column. See CSV format
Maps an agent variable to a CSV column, as
{ "agent_variable": "csv_column" }. Both are lowercase keys. The column must exist in the CSVFallback text for an agent variable, as
{ "agent_variable": "text" }. Up to 500 characters each. Used when the row has no valueResponse
201 Created with the campaign. 200 OK with the first campaign when theIdempotency-Key and body match an earlier request.
The response has the same fields as Get campaign, with status set to running or scheduled.
{
"success": true,
"data": {
"id": "3f1c9a2e-7b44-4d0e-9a5f-1c2d3e4f5a6b",
"name": "Spring renewals",
"agent_id": "9c2e1d7a-0b3f-4e6a-8f1d-5a7b9c0e2d44",
"agent_name": "Front desk",
"phone_line_id": "2d8e4f10-6a3b-4c9d-8e7f-0b1a2c3d4e5f",
"phone_line_name": "Main line",
"status": "scheduled",
"pause_reason": null,
"timezone": "Asia/Kolkata",
"start_at": "2026-12-01T03:30:00.000Z",
"calling_windows": [{ "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "20:00" }],
"concurrency_limit": 3,
"max_attempts": 3,
"retry_delay_secs": 1800,
"max_consecutive_failures": null,
"consecutive_failures": 0,
"variable_mapping": {},
"variable_defaults": {},
"counts": { "total": 250, "pending": 247, "calling": 0, "completed": 0, "no_answer": 0, "failed": 0, "skipped": 3, "cancelled": 0 },
"created_at": "2026-11-12T09:00:00.000Z",
"updated_at": "2026-11-12T09:00:00.000Z",
"started_at": null,
"completed_at": null
}
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | A body field or the Idempotency-Key header is invalid. field names it |
| 400 | CAMPAIGN_INVALID | The request is valid in shape but not usable. field names it. See the list below |
| 403 | POLICY_DENY | No active plan, or your role cannot create campaigns |
| 404 | AGENT_NOT_FOUND | No agent with this agent_id in your workspace |
| 404 | PHONE_LINE_NOT_FOUND | No phone line with this phone_line_id in your workspace |
| 409 | LINE_NOT_READY | The phone line is not ready to place calls |
| 409 | IDEMPOTENCY_CONFLICT | This Idempotency-Key was used for a different request |
| 429 | RATE_LIMITED | More than 10 campaigns created in the last minute |
CAMPAIGN_INVALID covers these cases:
- The CSV has no phone column, has a duplicate header, has more than 20,000 rows, is larger than 5 MB, or cannot be parsed.
fieldiscsv_text. - No row can be called after the do-not-call check.
fieldiscsv_text. startis not in the future.fieldisstart.concurrency_limitis outside 1 to 50 on your own line.fieldisconcurrency_limit.variable_mappingorvariable_defaultshas a bad key or value, or maps to a column the CSV does not have.fieldisvariable_mappingorvariable_defaults.
Example
curl -X POST https://api.60db.ai/agents/campaigns \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: spring-renewals-2026-11-12" \
-d '{
"name": "Spring renewals",
"agent_id": "AGENT_ID",
"phone_line_id": "PHONE_LINE_ID",
"timezone": "Asia/Kolkata",
"start": "now",
"calling_windows": [{ "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "20:00" }],
"max_attempts": 3,
"retry_delay_secs": 1800,
"max_consecutive_failures": 10,
"variable_mapping": { "customer_name": "name" },
"csv_text": "phone_number,name\n+919876543210,Asha\n+919812345678,Ravi\n"
}'