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

# Campaigns

> Create and run outbound calling campaigns.

A campaign places outbound calls to a set of contacts. It ties together the agent that speaks on the call, the phone number the call comes from, and the contact lists that define who to call. Once created, you control the campaign with the start, pause, resume, and cancel operations.

## Base URL

```
https://api.persistence.dev
```

<Note>
  These endpoints are served under the public `/v1` prefix.
</Note>

## Authentication

Every request requires a workspace API key in the `X-API-Key` header.

```bash theme={null}
curl https://api.persistence.dev/v1/calls/campaigns \
  --header "X-API-Key: $PERSISTENCE_API_KEY"
```

## Endpoints

<CardGroup cols={1}>
  <Card title="List campaigns" icon="list" href="/api-reference/campaigns/list-campaigns">
    `GET /v1/calls/campaigns` — page through your campaigns.
  </Card>

  <Card title="Create campaign" icon="plus" href="/api-reference/campaigns/create-campaign">
    `POST /v1/calls/campaigns` — create a campaign.
  </Card>

  <Card title="Get campaign" icon="file-text" href="/api-reference/campaigns/get-campaign">
    `GET /v1/calls/campaigns/{id}` — retrieve a campaign and its contact groups.
  </Card>

  <Card title="Update campaign" icon="pencil" href="/api-reference/campaigns/update-campaign">
    `PATCH /v1/calls/campaigns/{id}` — change a campaign's configuration.
  </Card>

  <Card title="Delete campaign" icon="trash-2" href="/api-reference/campaigns/delete-campaign">
    `DELETE /v1/calls/campaigns/{id}` — delete a campaign.
  </Card>

  <Card title="List campaign contacts" icon="users" href="/api-reference/campaigns/list-campaign-contacts">
    `GET /v1/calls/campaigns/{id}/contacts` — see per-contact call status.
  </Card>

  <Card title="List campaign conversations" icon="messages-square" href="/api-reference/campaigns/list-campaign-events">
    `GET /v1/calls/campaigns/{id}/events` — page through conversations produced by the campaign.
  </Card>

  <Card title="Start campaign" icon="play" href="/api-reference/campaigns/start-campaign">
    `POST /v1/calls/campaigns/{id}/start` — begin placing calls.
  </Card>

  <Card title="Pause campaign" icon="pause" href="/api-reference/campaigns/pause-campaign">
    `POST /v1/calls/campaigns/{id}/pause` — pause a running campaign.
  </Card>

  <Card title="Resume campaign" icon="play" href="/api-reference/campaigns/resume-campaign">
    `POST /v1/calls/campaigns/{id}/resume` — resume a paused campaign.
  </Card>

  <Card title="Cancel campaign" icon="x" href="/api-reference/campaigns/cancel-campaign">
    `POST /v1/calls/campaigns/{id}/cancel` — cancel a campaign.
  </Card>
</CardGroup>

## Before you start

A campaign references resources created through the other APIs. Gather these first.

<CardGroup cols={3}>
  <Card title="Agent" icon="bot" href="/api-reference/agents/overview">
    `agentId` — the agent that speaks on the call.
  </Card>

  <Card title="Phone number" icon="phone" href="/api-reference/telephony/overview">
    `fromNumber` — the number the call comes from, or use a number pool.
  </Card>

  <Card title="Contact lists" icon="users" href="/api-reference/campaign-audience/overview">
    `contactListIds` and `dndListIds` — who to call and who to skip.
  </Card>
</CardGroup>

## Create a campaign

`POST /v1/calls/campaigns` requires `name`, `agentId`, `contactListIds`, `maxConcurrency`, and exactly one of `fromNumber` or `numberPoolId`.

| Field            | Type            | Description                                                                    |
| ---------------- | --------------- | ------------------------------------------------------------------------------ |
| `name`           | string          | Required. Campaign name.                                                       |
| `agentId`        | string          | Required. Agent that places the calls.                                         |
| `fromNumber`     | string          | Caller ID in E.164 format. Mutually exclusive with `numberPoolId`.             |
| `numberPoolId`   | string          | Number pool used for caller ID rotation. Mutually exclusive with `fromNumber`. |
| `contactListIds` | array of string | Required. Contact lists to call. Must contain at least one ID.                 |
| `maxConcurrency` | integer         | Required. Maximum simultaneous calls. Minimum `1`.                             |
| `dndListIds`     | array of string | Contact lists whose numbers the campaign skips.                                |
| `retryPolicy`    | object          | Retry configuration for campaign calls.                                        |
| `scheduledAt`    | date-time       | Required unless `sendNow` is `true`.                                           |
| `scheduleStart`  | string          | Optional daily calling-window start in zero-padded `HH:MM`.                    |
| `scheduleEnd`    | string          | Optional daily calling-window end in zero-padded `HH:MM`.                      |
| `timezone`       | string          | IANA time zone used for the schedule.                                          |
| `sendNow`        | boolean         | Start the campaign immediately instead of scheduling it.                       |

Pass `contactListIds` for the audience and `dndListIds` for numbers to exclude. Both accept IDs from the [Campaign audience API](/api-reference/campaign-audience/overview); create do-not-disturb lists with `type` set to `dnd`.

```bash theme={null}
curl --request POST https://api.persistence.dev/v1/calls/campaigns \
  --header "X-API-Key: $PERSISTENCE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Spring outreach",
    "agentId": "AGENT_ID",
    "fromNumber": "+15551234567",
    "contactListIds": ["CONTACT_LIST_ID"],
    "maxConcurrency": 5
  }'
```

A `422` indicates the campaign could not be created with the values supplied.

<Note>
  Set `scheduleStart` and `scheduleEnd` together. Do not send scheduling fields when `sendNow` is `true`.
</Note>

## Control a running campaign

Four operations move a campaign through its lifecycle. Each takes only the campaign ID and returns the updated campaign.

| Operation | Endpoint                               |
| --------- | -------------------------------------- |
| Start     | `POST /v1/calls/campaigns/{id}/start`  |
| Pause     | `POST /v1/calls/campaigns/{id}/pause`  |
| Resume    | `POST /v1/calls/campaigns/{id}/resume` |
| Cancel    | `POST /v1/calls/campaigns/{id}/cancel` |

```bash theme={null}
curl --request POST \
  "https://api.persistence.dev/v1/calls/campaigns/$CAMPAIGN_ID/start" \
  --header "X-API-Key: $PERSISTENCE_API_KEY"
```

The campaign records `pausedAt`, `resumedAt`, `stoppedAt`, and `cancelledAt` as these operations are applied, so you can read the current state from the campaign itself.

## Campaign fields

| Field                  | Type             | Description                                                             |
| ---------------------- | ---------------- | ----------------------------------------------------------------------- |
| `id`                   | string           | Campaign ID.                                                            |
| `name`                 | string           | Campaign name.                                                          |
| `description`          | string           | Campaign description.                                                   |
| `status`               | string           | Current campaign status.                                                |
| `direction`            | string           | Call direction, `inbound` or `outbound`.                                |
| `agentId`              | string           | Agent that places the calls.                                            |
| `fromNumber`           | string           | Number the calls come from.                                             |
| `lists`                | array            | Contact lists attached to the campaign, each with an `id` and a `role`. |
| `maxConcurrency`       | integer          | Maximum simultaneous calls.                                             |
| `retryPolicy`          | array of integer | Retry schedule for unanswered calls.                                    |
| `totalContacts`        | integer          | Number of contacts in the campaign.                                     |
| `targetCallCount`      | integer          | Target number of calls.                                                 |
| `targetConversionRate` | number           | Target conversion rate.                                                 |
| `scheduledAt`          | string           | When the campaign is scheduled to run.                                  |
| `scheduleStart`        | integer          | Start of the daily calling window.                                      |
| `scheduleEnd`          | integer          | End of the daily calling window.                                        |
| `timezone`             | string           | Timezone the schedule is interpreted in.                                |
| `startDate`            | string           | Campaign start date.                                                    |
| `endDate`              | string           | Campaign end date.                                                      |
| `pausedAt`             | string           | Time the campaign was paused.                                           |
| `resumedAt`            | string           | Time the campaign was resumed.                                          |
| `stoppedAt`            | string           | Time the campaign stopped.                                              |
| `cancelledAt`          | string           | Time the campaign was cancelled.                                        |
| `meta`                 | object           | Additional campaign metadata.                                           |
| `createdAt`            | string           | Creation timestamp.                                                     |
| `updatedAt`            | string           | Last update timestamp.                                                  |

When a campaign fails, `meta.failure.reason` describes why.

`GET /v1/calls/campaigns/{id}` returns these fields plus `agentName` and a `contactGroups` array, where each group has an `id`, `name`, and `type`.

## Campaign contacts

`GET /v1/calls/campaigns/{id}/contacts` lists the individual contacts in a campaign.

| Field         | Type    | Description                   |
| ------------- | ------- | ----------------------------- |
| `phoneNumber` | string  | The contact's phone number.   |
| `status`      | string  | Call status for this contact. |
| `attempts`    | integer | Number of call attempts made. |
| `createdAt`   | string  | Creation timestamp.           |
| `updatedAt`   | string  | Last update timestamp.        |

The endpoint accepts `page`, `perPage`, and a `status` filter.

## Pagination and filtering

`GET /v1/calls/campaigns` accepts these query parameters.

| Parameter    | Type    | Description                                           |
| ------------ | ------- | ----------------------------------------------------- |
| `page`       | integer | Page number.                                          |
| `perPage`    | integer | Items per page.                                       |
| `status`     | string  | Filter by status: `active`, `paused`, or `completed`. |
| `direction`  | string  | Filter by direction: `inbound` or `outbound`.         |
| `name`       | string  | Filter by campaign name.                              |
| `searchText` | string  | Filter campaigns by text.                             |

Both list endpoints return a paged object containing `items`, `page`, `perPage`, `totalCount`, `column`, `order`, and `searchText`.

## Errors

Errors return a `code` and an `error` message.

```json theme={null}
{
  "code": "not_found",
  "error": "campaign not found"
}
```

| Status | Meaning                                                     |
| ------ | ----------------------------------------------------------- |
| `400`  | The request body or a path parameter is invalid.            |
| `401`  | The `X-API-Key` header is missing or invalid.               |
| `403`  | The key is not permitted to perform the operation.          |
| `404`  | The campaign does not exist.                                |
| `422`  | The campaign could not be created with the values supplied. |
