Campaigns

Create outbound campaigns and run them at scale.

A campaign dials a list of contacts with a published agent, on a schedule and within your compliance window. The typical flow: create → set contacts → publish → run, then poll the run for progress.

Campaign object:

{
  "id": "cmp_…",
  "name": "August outreach",
  "channel": "voice",
  "workflowType": "collections",
  "dltTemplateRef": null,
  "agentId": "agt_…",
  "status": "draft",
  "settings": { "fromNumber": "+91…", "timezone": "Asia/Kolkata", "startAt": null, "endAt": null,
                "dailyFrom": "10:00", "dailyTo": "19:00", "retryCount": 2, "cooldownMinutes": 30,
                "emailNotifications": [] },
  "callingList": null
}

Run object:

{
  "id": "run_…", "campaignId": "cmp_…", "status": "running",
  "attempted": 120, "placed": 96, "rejected": 24,
  "skippedReason": null, "error": null,
  "createdAt": "2026-08-20T10:00:00Z", "finishedAt": null
}

List campaigns

GET /v1/campaigns  ·  scope campaigns:read{ "data": campaign[] } (all in the workspace).

Get a campaign

GET /v1/campaigns/:id  ·  scope campaigns:read{ "data": campaign }. Unknown → 404 campaign not found.


Create a campaign

POST /v1/campaigns  ·  scope campaigns:write

Body

Field Type Default / constraints
name string max 120, default ""
channel enum voice, sms, whatsapp, email, ivr - default voice
workflowType enum collections, onboarding - default collections
agentId string | null default null
dltTemplateRef string | null default null
settings object see below

settings: fromNumber (string|null), timezone (valid IANA tz|null), startAt/endAt/dailyFrom/dailyTo (string|null), retryCount (int 0–10, default 0), cooldownMinutes (int ≥0, default 0), emailNotifications (string[] of emails, default []).

Validation

  • If agentId is set, the agent must exist and be published - else 422 agent must be published before it can run a campaign.
  • If settings.fromNumber is set, it must be an active number you own - else 400 from number is not an active number you own.

Response 201: { "data": campaign } (status: "draft", callingList: null).

Update a campaign

PUT /v1/campaigns/:id  ·  scope campaigns:write

PATCH semantics - send only what changes: name, agentId, dltTemplateRef, settings (each sub-field optional, merged over current). The agent-published and owned-number checks run only for the fields you send. Unknown → 404.


Set the calling list

POST /v1/campaigns/:id/contacts  ·  scope campaigns:write  ·  rate limited

Replaces the entire calling list.

{
  "contacts": [
    { "phone": "+919812345678", "name": "Nalin", "variables": { "amount": "4999" } },
    { "phone": "+919812345679", "name": "Asha" }
  ]
}
  • contacts - 1 to 50,000 rows. Each row: phone (8–20, required), name (optional), variables (optional object).
  • Each phone is validated; duplicates keep the first occurrence. Invalid and duplicate rows are stored flagged and skipped at dial time.

Response 200:

{ "data": { "total": 2, "valid": 2, "invalid": 0 } }

Publish / pause

POST /v1/campaigns/:id/status  ·  scope campaigns:write

{ "status": "active" }   // "draft" | "active" | "paused"

Publishing (active) requires the attached agent to be published (422 otherwise). Response 200: { "data": campaign }.

Start a run

POST /v1/campaigns/:id/run  ·  scope campaigns:write  ·  rate limited  ·  no body

Kicks off background dialing.

Response 202:

{ "data": { "runId": "run_…", "status": "queued" } }
  • 403 workspace suspended
  • 404 campaign not found
  • 409 campaign must be published before it can run (status isn’t active)

Runs

  • GET /v1/campaigns/:id/runs  ·  scope campaigns:read{ "data": run[] }. Unknown campaign → 404.
  • GET /v1/campaign-runs/:runId  ·  scope campaigns:read{ "data": run }. Unknown → 404 run not found.

Poll a run’s status, attempted/placed/rejected counts, and skippedReason (e.g. outside_schedule, kyb_unverified, wallet) to track progress.

Delete a campaign

DELETE /v1/campaigns/:id  ·  scope campaigns:write{ "data": { "id": "cmp_…", "deleted": true } }.

  • 409 pause the campaign before deleting it (active campaigns can’t be deleted)
  • 404 campaign not found

Next: Phone numbers →