> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dialbird.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Campaigns

> Load leads into dialer campaigns and get every call's outcome back.

A **campaign** is a list of contacts your team works through with the Dialbird power or parallel dialer. With the API you can do two things:

1. **Load leads in.** Create campaigns and add contacts from your CRM, Clay, Zapier or anywhere else. Reps then open the campaign in Dialbird and start dialing.
2. **Get outcomes out.** Subscribe to `campaign.call.completed` to receive each call's result (its disposition, note, contact and call details) as soon as it is known.

Campaign endpoints need the `api:campaigns:read` or `api:campaigns:write` scope. They also need the dialer on the workspace's plan; otherwise every campaign endpoint returns `403 forbidden`.

## Load leads into a campaign

Create a campaign, then add contacts to it.

Every campaign calls and texts from the inboxes you give it in `inbox_ids`: its dialer sessions and SMS broadcasts can use only those numbers. Get the ids from `GET /inboxes`.

```bash theme={null}
curl https://app-staging.dialbird.io/api/v1/inboxes \
  -H "Authorization: Bearer $DIALBIRD_TOKEN"
```

```bash theme={null}
curl -X POST https://app-staging.dialbird.io/api/v1/campaigns \
  -H "Authorization: Bearer $DIALBIRD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Q4 outbound", "inbox_ids": ["'"$INBOX_ID"'"] }'
```

* `inbox_ids` is **required**: 1 to 10 inboxes, in the order the dialer rotates through them. A request without it returns `400 validation_error` on `inbox_ids`.
* The token acts as the user who created it. An admin's token can use any inbox. A member's token can use only the inboxes that member is assigned to: `GET /inboxes` lists every inbox in the workspace, and an inbox the member isn't on returns `403 forbidden`.
* Admins can change a campaign's numbers later in Dialbird.

<Note>
  `inbox_ids` became required on campaign creation. Integrations that create campaigns with only a `name` must now send it.
</Note>

```bash theme={null}
curl -X POST https://app-staging.dialbird.io/api/v1/campaigns/$CAMPAIGN_ID/contacts \
  -H "Authorization: Bearer $DIALBIRD_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lead-42" \
  -d '{
    "contacts": [
      {
        "first_name": "Ada",
        "last_name": "Lovelace",
        "company": "Analytical Engines",
        "phones": ["+15551234567"],
        "extra_fields": { "crm_id": "42" }
      }
    ]
  }'
```

```json theme={null}
{ "imported": 1, "duplicates": 0, "skipped": 0, "import_id": "…" }
```

* Send **1 to 500 contacts** per request.
* Each contact needs 1–5 `phones`, in the order they should be dialed. Use E.164, or national format in your workspace's country.
* A contact whose first usable number is already in the campaign counts as a **duplicate** and is not added again. A contact with no usable number is **skipped**.
* `extra_fields` keeps any other columns as text. Reps see them in the dialer, and they come back on the contact and in the webhook.
* Everything you add through the API appears as one "API" entry in the campaign's import history in Dialbird.

<Tip>
  Send an `Idempotency-Key` (for example your CRM's lead id) so a retried request doesn't add the lead twice. Keys are scoped per campaign, so the same key can add the same lead to several campaigns.
</Tip>

You can also read a campaign's contacts (`GET /campaigns/{id}/contacts`, filterable by `status` and `disposition_id`), update one (for example set `"status": "do_not_call"`), or remove one. See the API reference.

## Call outcome webhook

Subscribe to `campaign.call.completed` the same way as any other [webhook](/concepts/webhooks). Dialbird sends one event for each dialer call that rang, once its outcome is final:

| The call | Sent when |
| - | - |
| Wasn't answered (no answer, busy, failed, voicemail, machine, canceled) | The dialing round ends, with the disposition the dialer set automatically, or `null` |
| Connected to a rep | The rep picks a disposition |

If the rep later changes the disposition, Dialbird sends the event again with the same `attempt_id`, so treat the latest one as current. A connected call nobody gives a disposition sends nothing.

```json theme={null}
{
  "id": "evt_…",
  "type": "campaign.call.completed",
  "api_version": "v1",
  "occurred_at": "2026-10-01T10:00:00Z",
  "business_id": "…",
  "data": {
    "attempt_id": "…",
    "outcome": "answered",
    "note": "Call back Tuesday",
    "disposition": {
      "id": "…",
      "name": "Meeting booked",
      "source": "dialbird",
      "system_key": null,
      "marks_contact_complete": true
    },
    "campaign": { "id": "…", "name": "Q4 outbound" },
    "campaign_contact": {
      "id": "…",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "phones": [{ "number": "+15551234567", "label": null }],
      "dialed_number": "+15551234567",
      "status": "completed",
      "extra_fields": { "crm_id": "42" },
      "…": "…"
    },
    "call": { "id": "…", "direction": "outgoing", "duration_seconds": 312, "…": "…" },
    "user": { "id": "…", "name": "Grace Hopper" }
  }
}
```

`outcome` is one of:

| Outcome | Meaning |
| - | - |
| `answered` | Connected to the rep. |
| `abandoned` | Answered after the rep was already on another call. |
| `voicemail_dropped` | Reached a machine and left the recorded voicemail. |
| `machine` | Reached a machine and hung up. |
| `no_answer` / `busy` / `failed` | Didn't connect. |
| `canceled` | Stopped ringing because another line answered or the dialer was stopped. |

<Tip>
  To act only on certain results (for example push to your CRM only when a meeting is booked), list your dispositions with `GET /dispositions` and filter on `data.disposition.id` in your automation.
</Tip>

The same call also sends `call.outgoing.completed`, so you can subscribe to just one of them.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.