> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.voicebip.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.voicebip.com/_mcp/server.

# Campaigns

> Paced outbound campaigns — reach a list of recipients over voice, SMS, or WhatsApp at a controlled, carrier-safe rate

Campaigns reach a list of recipients with one of your AI agents over **voice,
SMS, or WhatsApp**, at a carrier-safe rate, with automatic retries for
no-answer/busy voice calls. You create a campaign, upload recipients, then start
it — Voicebip paces the dispatch per carrier and records the outcome of every
recipient. This guide leads with voice; [SMS and WhatsApp](#5-sms-and-whatsapp-campaigns)
use the same flow with a different `config`.

The lifecycle is: **draft → running ⇄ paused → completed | cancelled**.

## 1. Create a campaign

A new campaign starts in `draft`. `config` carries the per-call template
(caller-id, optional webhook, optional call window). `retry_policy` is optional —
omit it for no retries.

```bash
curl -X POST "https://api.voicebip.com/v1/campaigns" \
  -H "Authorization: Bearer pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "voice",
    "agent_id": "agt_abc123",
    "config": {
      "from_number": "+2348001234567",
      "call_window_start": "09:00",
      "call_window_end": "18:00",
      "call_window_timezone": "Africa/Lagos"
    },
    "retry_policy": {
      "max_attempts": 3,
      "backoff_seconds": 300,
      "backoff_multiplier": 2.0
    }
  }'
```

Response (`201`):

```json
{
  "id": "camp_abc123",
  "channel": "voice",
  "agent_id": "agt_abc123",
  "status": "draft",
  "config": { "from_number": "+2348001234567", "...": "..." },
  "retry_policy": { "max_attempts": 3, "backoff_seconds": 300, "backoff_multiplier": 2.0 },
  "counters": { "total": 0, "queued": 0, "dispatched": 0, "succeeded": 0, "failed": 0 },
  "created_at": "2026-06-11T12:00:00Z"
}
```

### Retry policy

| Field                | Default | Meaning                                                                                   |
| -------------------- | ------- | ----------------------------------------------------------------------------------------- |
| `max_attempts`       | `3`     | Total dial attempts per recipient (including the first).                                  |
| `backoff_seconds`    | `300`   | Base wait before the first retry.                                                         |
| `backoff_multiplier` | `2.0`   | Exponential factor: wait = `backoff_seconds × multiplier^(attempt-1)`, capped at 6 hours. |

Only **transient** failures (`no_answer`, `busy`) are retried. `completed` and
hard `failed` outcomes are terminal. **Omit `retry_policy` entirely** to disable
retries — every recipient is dialed once.

## 2. Add recipients

Upload recipients in bulk (up to 10,000 per request). Invalid E.164 numbers and
in-campaign duplicates are skipped, not fatal — the response reports the counts.
`params` are optional per-recipient template variables.

```bash
curl -X POST "https://api.voicebip.com/v1/campaigns/camp_abc123/targets" \
  -H "Authorization: Bearer pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "targets": [
      { "to_address": "+2348031234567", "params": { "first_name": "Ada" } },
      { "to_address": "+2348039876543", "params": { "first_name": "Bola" } }
    ]
  }'
```

Response (`200`):

```json
{ "added": 2, "skipped_duplicate": 0, "rejected_invalid": 0 }
```

## 3. Start, pause, resume, cancel

```bash
# Begin paced dialing (draft → running)
curl -X POST "https://api.voicebip.com/v1/campaigns/camp_abc123/start" \
  -H "Authorization: Bearer pk_live_your_key"

# Halt new dials; in-flight calls finish (running → paused)
curl -X POST "https://api.voicebip.com/v1/campaigns/camp_abc123/pause" \
  -H "Authorization: Bearer pk_live_your_key"

# Re-seed the remaining recipients (paused → running)
curl -X POST "https://api.voicebip.com/v1/campaigns/camp_abc123/resume" \
  -H "Authorization: Bearer pk_live_your_key"

# Stop and discard the backlog (any non-terminal status → cancelled)
curl -X POST "https://api.voicebip.com/v1/campaigns/camp_abc123/cancel" \
  -H "Authorization: Bearer pk_live_your_key"
```

A campaign **auto-completes** (`running → completed`) once every recipient has a
terminal outcome and no retries remain.

## 4. Track progress

Poll the campaign for live counters, or list per-recipient outcomes.

```bash
# Campaign-level progress
curl "https://api.voicebip.com/v1/campaigns/camp_abc123" \
  -H "Authorization: Bearer pk_live_your_key"
```

```json
{
  "id": "camp_abc123",
  "status": "running",
  "counters": { "total": 500, "queued": 120, "dispatched": 3, "succeeded": 340, "failed": 37 }
}
```

`total = queued + dispatched + succeeded + failed` always holds:

* **queued** — waiting to be dialed (first attempt or a scheduled retry).
* **dispatched** — currently in flight, awaiting outcome.
* **succeeded** — call completed.
* **failed** — hard failure, or a transient failure that exhausted its retries.

```bash
# Per-recipient outcomes (filter by disposition, paginate)
curl "https://api.voicebip.com/v1/campaigns/camp_abc123/targets?disposition=no_answer" \
  -H "Authorization: Bearer pk_live_your_key"
```

```json
{
  "targets": [
    {
      "id": "tgt_xyz789",
      "to_address": "+2348031234567",
      "disposition": "no_answer",
      "attempt_count": 2,
      "next_attempt_at": "2026-06-11T12:10:00Z",
      "last_exec_id": "call_def456"
    }
  ],
  "next_page_token": ""
}
```

Every campaign-dialed call also appears in `GET /v1/calls` with its `campaign_id`
set, so transcripts and recordings flow through the usual call APIs.

## 5. SMS and WhatsApp campaigns

Campaigns aren't voice-only. Set `channel` to `sms` or `whatsapp` and Voicebip
paces the sends the same way it paces dials — per carrier for SMS, per sender +
template-category for WhatsApp. The lifecycle, target upload, tracking, and
pagination are **identical** to voice; only `config` changes per channel.

| Channel    | `config` fields                                                                                       | Paced by                          | Notes                                                                                                                                                                 |
| ---------- | ----------------------------------------------------------------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `voice`    | `from_number`, optional call-window                                                                   | per-carrier concurrency           | retries apply (`no_answer`/`busy`).                                                                                                                                   |
| `sms`      | `from_number`, `body`                                                                                 | per-carrier throughput            | one campaign = one message body; per-recipient override via target `params.body`.                                                                                     |
| `whatsapp` | `from_number`, `phone_number_id`, `template_name`, `language_code`, `category`, optional `parameters` | per-sender daily conversation cap | WhatsApp campaigns are **template** campaigns; `category` is `marketing`/`utility`/`authentication`. Per-recipient template variables via target `params.parameters`. |

**SMS campaign:**

```bash
curl -X POST "https://api.voicebip.com/v1/campaigns" \
  -H "Authorization: Bearer pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "agent_id": "agt_abc123",
    "config": { "from_number": "+2348001234567", "body": "Hi {{first_name}}, your order shipped." }
  }'
```

**WhatsApp campaign** (one approved template, one sender, one category):

```bash
curl -X POST "https://api.voicebip.com/v1/campaigns" \
  -H "Authorization: Bearer pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp",
    "agent_id": "agt_abc123",
    "config": {
      "from_number": "+2348001234567",
      "phone_number_id": "1234567890",
      "template_name": "order_update",
      "language_code": "en",
      "category": "utility"
    }
  }'
```

Recipients who have **opted out** are suppressed automatically — per-category or
blanket for WhatsApp, blanket for SMS — and recorded with disposition
`opted_out`; they are never submitted to the carrier. A messaging recipient is marked `sent` once the
provider accepts the submit. For SMS, the carrier delivery receipt then upgrades
that to `delivered`, or to `undeliv` on a hard delivery failure. For WhatsApp,
Meta upgrades it to `delivered` / `read`, or `failed`. All of this is visible on
the target's `disposition`.

## Notes

* Pacing and the per-carrier concurrency cap are managed by Voicebip — you don't
  set a dial rate; the platform stays within carrier limits.
* `config`, `schedule`, and `retry_policy` are free-form JSON objects. The
  `voice`, `sms`, and `whatsapp` channels are supported for campaigns; automatic
  retries apply to the `voice` channel today.
* List endpoints use opaque-cursor pagination (`next_page_token`).