Docs · Build · Campaigns

Campaigns.

A campaign fans one published agent out to many recipients over a single phone number, each call carrying its own vars and running at a concurrency you set. The platform paces the dialer, respects your calling window, and retires the batch when the list is drained — and every call still fires call.completed with its recording, transcript, tool log, and outcome.

Create a campaign#

POST/v1/campaigns
FieldTypeDescription
namestringoptionalHuman label for the dashboard. Defaults to "Untitled campaign".
agent_idstringrequiredThe agent to run. Must be published — an unpublished agent returns 409 agent_not_published.
phone_number_idstringrequiredThe number every call is placed from. It must be outbound-enabled.
recipientsobject[]requiredThe call list — each entry is { to, vars }. to is an E.164 number; varsis a free-form object merged into that recipient's call and available to the agent's prompt and tools.
target_concurrencyintegeroptionalHow many calls run in parallel. Defaults to a value within your plan's included lines and is capped there — see pricing.
scheduleobjectoptionalWhen calls may go out: { start_at, calling_window: { tz, from, to } }. from and to are 24-hour local times (e.g. "09:00", "20:00") in the window's tz. Omitted, the campaign dials as soon as it is started.
Request
curl -X POST https://voice.whizztech.ai/v1/campaigns \
  -H "Authorization: Bearer $WHIZZ_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "July renewals · AR",
    "agent_id": "ag_7Q2c51b84a07",
    "phone_number_id": "pn_5b8f0d21",
    "target_concurrency": 5,
    "recipients": [
      { "to": "+966500000001", "vars": { "name": "سارة", "order": "A-2291" } },
      { "to": "+966500000002", "vars": { "name": "خالد", "order": "A-2318" } }
    ],
    "schedule": {
      "start_at": "2026-07-14T07:00:00.000Z",
      "calling_window": { "tz": "Asia/Riyadh", "from": "09:00", "to": "20:00" }
    }
  }'
Response · 201 Created
{
  "id": "camp_3f9a1c07",
  "object": "campaign",
  "agent_id": "ag_7Q2c51b84a07",
  "phone_number_id": "pn_5b8f0d21",
  "name": "July renewals · AR",
  "status": "draft",
  "target_concurrency": 5,
  "totals": {
    "recipients": 2,
    "queued": 0,
    "in_progress": 0,
    "completed": 0,
    "failed": 0
  },
  "created_at": "2026-07-13T09:32:18.000Z"
}

A new campaign lands in draftwith its recipients staged but nothing dialed. Add more recipients or start it when you're ready.

Add recipients#

POST/v1/campaigns/{id}/recipients

Top up a campaign — before or during a run — by posting more entries. The body is { recipients: [{ to, vars }] }. New entries queue behind the current list; on a running campaign they start dialing as concurrency frees up, no restart needed.

Request
curl -X POST https://voice.whizztech.ai/v1/campaigns/camp_3f9a1c07/recipients \
  -H "Authorization: Bearer $WHIZZ_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipients": [
      { "to": "+966500000003", "vars": { "name": "منى", "order": "A-2402" } },
      { "to": "+966500000004", "vars": { "name": "فهد", "order": "A-2410" } }
    ]
  }'
Response · 200 OK
{
  "id": "camp_3f9a1c07",
  "object": "campaign",
  "status": "running",
  "added": 2,
  "totals": {
    "recipients": 4,
    "queued": 2,
    "in_progress": 5,
    "completed": 0,
    "failed": 0
  }
}

Start & pause#

POST/v1/campaigns/{id}/start
POST/v1/campaigns/{id}/pause
Start the campaign
curl -X POST https://voice.whizztech.ai/v1/campaigns/camp_3f9a1c07/start \
  -H "Authorization: Bearer $WHIZZ_KEY"

start moves the campaign from draft to running and the dialer begins pacing calls up to target_concurrency. pause holds the line — in-flight calls finish, nothing new is placed — and start resumes it. The campaign flips to completed on its own once every recipient has been attempted.

FieldTypeDescription
draftstatusCreated, recipients staged, nothing dialed yet.
runningstatusDialing, paced up to target_concurrency within the calling window.
pausedstatusHeld. In-flight calls finish; no new calls are placed until you start it again.
completedstatusEvery recipient has been attempted. Terminal — create a new campaign to call again.

List & get#

GET/v1/campaigns

Cursor-paginated, newest-first. Filter by status.

Response · 200 OK
{
  "object": "list",
  "data": [
    {
      "id": "camp_3f9a1c07",
      "object": "campaign",
      "agent_id": "ag_7Q2c51b84a07",
      "name": "July renewals · AR",
      "status": "running",
      "target_concurrency": 5,
      "totals": {
        "recipients": 4,
        "queued": 0,
        "in_progress": 3,
        "completed": 1,
        "failed": 0
      },
      "created_at": "2026-07-13T09:32:18.000Z"
    }
  ],
  "has_more": false
}
GET/v1/campaigns/{id}

Returns the full campaign object. Poll it to watch the totals move — queued drains into in_progress, then into completed or failed — as the batch runs:

Response · 200 OK
{
  "id": "camp_3f9a1c07",
  "object": "campaign",
  "agent_id": "ag_7Q2c51b84a07",
  "phone_number_id": "pn_5b8f0d21",
  "name": "July renewals · AR",
  "status": "running",
  "target_concurrency": 5,
  "totals": {
    "recipients": 4,
    "queued": 0,
    "in_progress": 3,
    "completed": 1,
    "failed": 0
  },
  "created_at": "2026-07-13T09:32:18.000Z"
}

Fields#

FieldTypeDescription
agent_idstringThe published agent every call in the campaign runs.
phone_number_idstringThe outbound number all calls are placed from.
statusstringdraft · running · paused · completed. Driven by start / pause and by the list draining.
target_concurrencyintegerMaximum calls in flight at once. Capped by your plan's included lines.
totalsobjectLive counters: { recipients, queued, in_progress, completed, failed }. recipients is the whole list; the rest sum to it as the run progresses.