> For the complete documentation index, see [llms.txt](https://docs.cekat.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cekat.ai/chat/broadcasts/api-create-broadcast-campaign.md).

# API Create Broadcast Campaign

**POST** `{BASE_URL}/api/campaigns`

Create a broadcast campaign that sends a WhatsApp template message to every recipient in a recipient list, the same as creating one from the dashboard. The request requires a sender inbox, a recipient list, and a template; a conversation label and a schedule time are optional. A successful request returns the campaign `id`, `campaign_name`, `status`, and `created_at`, and the campaign is immediately visible on the dashboard under **Broadcast Settings > Campaign**.

***

### Create Broadcast Campaign Method

<table><thead><tr><th width="117.66796875">HTTP Method</th><th width="267.21484375">Endpoint</th><th>Description</th></tr></thead><tbody><tr><td>POST</td><td><code>{BASE_URL}/api/campaigns</code></td><td>Create a broadcast campaign</td></tr></tbody></table>

***

### Create Broadcast Campaign Request

**Request Headers**

```
Content-Type: application/json
authorization: api_key {{YOUR_API_KEY}}
```

**Sample Request**

```json
{
  "campaign_name": "Promo Oktober 2026",
  "inbox_id": "a1b2c3d4-0000-1111-2222-333344445555",
  "recipient_list_id": "f6e5d4c3-9999-8888-7777-666655554444",
  "template_id": "11223344-5566-7788-99aa-bbccddeeff00",
  "label_id": "22334455-6677-8899-aabb-ccddeeff0011",
  "schedule_at": "2026-10-15T09:00:00+07:00",
  "template_body_variables": ["name", "additional_data.umur", "PROMO17"]
}
```

<table><thead><tr><th width="224.78515625">JSON Attribute</th><th width="255.296875">Description</th><th width="111.421875">Type</th><th width="126.73046875">Required</th></tr></thead><tbody><tr><td><code>campaign_name</code></td><td>Name of the campaign.</td><td>String</td><td>Required</td></tr><tr><td><code>inbox_id</code></td><td>ID of the sender inbox. Only WhatsApp inboxes are supported. Use <code>GET /api/inboxes</code> to retrieve valid IDs.</td><td>String</td><td>Required</td></tr><tr><td><code>recipient_list_id</code></td><td>ID of the recipient list to send to. Raw phone numbers are not accepted directly; recipients must come from a recipient list. Use <code>GET /api/recipients</code> to retrieve valid IDs.</td><td>String</td><td>Required</td></tr><tr><td><code>template_id</code></td><td>ID of the WhatsApp template to send. Use <code>GET /api/templates</code> to retrieve valid IDs.</td><td>String</td><td>Required</td></tr><tr><td><code>label_id</code></td><td>ID of the conversation label applied to every conversation this campaign generates. Use <code>GET /api/labels</code> to retrieve valid IDs.</td><td>String</td><td>Optional</td></tr><tr><td><code>schedule_at</code></td><td>Scheduled send time, ISO 8601. If omitted, the campaign sends immediately. Cannot be a past datetime.</td><td>String (datetime)</td><td>Optional</td></tr><tr><td><code>template_body_variables</code></td><td>Ordered array of values for the variables in the template body (<code>{{1}}</code>, <code>{{2}}</code>, ...), array position maps to variable index. See Resolving <code>template_body_variables</code> below for how each entry is read. Required only if the template body has variables; array length must equal the number of variables in the template body.</td><td>Array of String</td><td>Conditional</td></tr></tbody></table>

#### Resolving `template_body_variables`

Each entry resolves to a value per recipient using one of three rules, based on how the entry is written:

| Entry format                                     | Resolves to                                                                                                                             | Example                                                                |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Exact column name, only `name` or `phone_number` | The recipient's value in that column, matched per recipient.                                                                            | `"name"` → that recipient's value in the `name` column                 |
| `additional_data.<column_name>`                  | The recipient's value in that column, for any column in the recipient list other than `phone_number` and `name`. Matched per recipient. | `"additional_data.umur"` → that recipient's value in the `umur` column |
| Anything else (free text)                        | Sent as-is, unchanged, to every recipient.                                                                                              | `"PROMO17"` → every recipient receives `PROMO17`                       |

**No silent fallback on typos:** if `additional_data.<column_name>` references a column that doesn't exist, for example `additional_data.umru` instead of `additional_data.umur`, it is not rejected and not skipped. It falls into the third rule above and is sent to the customer exactly as written, typo included. Double check column names before sending.

**cURL Example**

```bash
curl --location '{BASE_URL}/api/campaigns' \
--header 'Content-Type: application/json' \
--header 'authorization: api_key {{YOUR_API_KEY}}' \
--data '{
  "campaign_name": "Promo Oktober 2026",
  "inbox_id": "a1b2c3d4-0000-1111-2222-333344445555",
  "recipient_list_id": "f6e5d4c3-9999-8888-7777-666655554444",
  "template_id": "11223344-5566-7788-99aa-bbccddeeff00",
  "schedule_at": "2026-10-15T09:00:00+07:00",
  "template_body_variables": ["name", "additional_data.umur", "PROMO17"]
}'
```

***

### Create Broadcast Campaign Response

**Success Response (201)**

```json
{
  "id": "d1e2f3a4-0000-1111-2222-333344445555",
  "campaign_name": "Promo Oktober 2026",
  "status": "processing",
  "created_at": "2026-10-01T10:04:00+07:00"
}
```

| JSON Attribute  | Description                                                                                | Type              |
| --------------- | ------------------------------------------------------------------------------------------ | ----------------- |
| `id`            | Campaign ID generated by Cekat.                                                            | String            |
| `campaign_name` | Campaign name, as submitted.                                                               | String            |
| `status`        | `processing` if the campaign sends immediately, `scheduled` if `schedule_at` was provided. | String            |
| `created_at`    | Campaign creation timestamp.                                                               | String (datetime) |

**Error Responses**

<table><thead><tr><th width="137.9296875">Status Code</th><th>When it happens</th></tr></thead><tbody><tr><td>401</td><td>Missing or invalid API key</td></tr><tr><td>422</td><td>A required field is missing, <code>inbox_id</code> / <code>recipient_list_id</code> / <code>template_id</code> is invalid, <code>schedule_at</code> is in the past, <code>template_body_variables</code> length does not match the number of variables in the template body, or an entry in <code>template_body_variables</code> is an empty string</td></tr><tr><td>503</td><td>Campaign failed to queue</td></tr></tbody></table>

**Sample 422: variable count mismatch**

```json
{
  "success": false,
  "message": "Template variable count does not match. Template expects 2, received 3."
}
```

***

### Notes

* Only WhatsApp inboxes are supported as `inbox_id`.
* The expected `template_body_variables` length is the highest variable index in the template body, so a repeated index (for example `{{1}}` used twice) counts once.
* `template_body_variables` doesn't cover variables in the template header (including media headers) or in URL buttons, and there is no resolution preview in the `201` response showing which entries matched a column versus which were sent as literals.
* `{BASE_URL}` is a placeholder. Confirm the production host with engineering before use.
