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

# Campaigns

> Outbound batches, contacts, recipient lists, and live status.

A **campaign** is a batch (`/v1/batches`): an agent, a telephony account, contacts, and a dial window. Draft → start → pause / resume / cancel. Campaigns that have already run cannot be deleted — hide them instead.

## Create and start

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  from kupe import Kupe

  client = Kupe()
  me = client.me()
  batch = client.campaigns.create(
      org_id=me.org_id,
      project_id=me.project_id,
      agent_id="agt_...",
      telephony_account_id="tel_...",
      name="EMI reminders",
      max_concurrent_calls=2,
  )
  client.campaigns.contacts.add(batch.id, contacts=[{"phone_number": "+9198XXXXXXXX"}])
  client.campaigns.start(batch.id)
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { Kupe } from "kupe-sdk";

  const kupe = new Kupe();
  const me = await kupe.me();
  const batch = await kupe.campaigns.create({
    org_id: me.org_id!,
    project_id: me.project_id!,
    agent_id: "agt_...",
    telephony_account_id: "tel_...",
    name: "EMI reminders",
    max_concurrent_calls: 2,
  });
  await kupe.campaigns.contacts.add(batch.id, {
    contacts: [{ phone_number: "+9198XXXXXXXX" }],
  });
  await kupe.campaigns.start(batch.id);
  ```

  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://x.kupe.in/v1/batches \
    -H "Authorization: Bearer sk-kupe-YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "org_id":"org_...","project_id":"prj_...",
      "agent_id":"agt_...","telephony_account_id":"tel_...",
      "name":"EMI reminders","max_concurrent_calls":2
    }'

  curl -X POST https://x.kupe.in/v1/batches/bat_.../contacts:bulk \
    -H "Authorization: Bearer sk-kupe-YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{"contacts":[{"phone_number":"+9198XXXXXXXX"}]}'

  curl -X POST https://x.kupe.in/v1/batches/bat_.../start \
    -H "Authorization: Bearer sk-kupe-YOUR_KEY"
  ```
</CodeGroup>

CSV upload uses `POST /v1/batches/{batch_id}/contacts` as `multipart/form-data` (column `phone_number` plus optional `{{variable}}` columns).

## Recipient lists

Reusable people, copied into a draft campaign in one SQL-side attach:

1. `POST /v1/recipient-lists`
2. `POST /v1/recipient-lists/{list_id}/members:bulk` (or CSV on `/members`)
3. `POST /v1/batches/{batch_id}/contacts:from-list`

## Schedule

`PATCH /v1/batches/{batch_id}/schedule` sets recurrence (`once` / `daily` / `weekly` / `monthly`), local `window_start` / `window_end`, timezone, and optional `limit_per_period`. It does **not** start the batch — the scheduler picks it up on the next tick.

## Control plane

| Action                          | Path                                                            |
| ------------------------------- | --------------------------------------------------------------- |
| Start / pause / resume / cancel | `POST /v1/batches/{id}/start` (and `pause`, `resume`, `cancel`) |
| Stats                           | `GET /v1/batches/{id}/stats`                                    |
| Live SSE                        | `GET /v1/batches/{id}/events` (`text/event-stream`)             |
| Hide                            | `POST /v1/batches/{id}/hide` — row stays; logs and usage stay   |
| Unhide all                      | `POST /v1/orgs/{org_id}/projects/{project_id}/batches:unhide`   |
| Delete                          | `DELETE /v1/batches/{id}` — **drafts that never started only**  |

Needs feature flags `feature_batch_calls` and `feature_outbound`. Buy a number first — see [Phones](/guides/phones).
