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

# Broadcast Campaigns

Send bulk email/SMS messages to targeted audiences. A campaign combines a **template** with an **audience**.

***

## Headers (every request)

```http theme={null}
Authorization: Bearer {token}
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
```

All endpoints include `?o={organization_id}` for tenant context.

***

## Concepts

| Resource | Type | Description |
| - | - | - |
| Campaign | `broadcast-campaigns` | Orchestrates sending; tracks status and progress |
| Template | `broadcast-templates` | Email/SMS content with i18n and variable placeholders |
| Audience | `broadcast-audiences` | Recipient targeting based on dynamic customer, booking, or community criteria |
| Message | `broadcast-messages` | Individual message instance per recipient |

### Status Lifecycle

`draft` → `scheduled` → `pending` → `in-progress` → `completed`

| Status | Meaning |
| - | - |
| `draft` | Not yet started; can be edited |
| `scheduled` | Queued for future send (`scheduled_at`) or next cron run |
| `pending` | About to be processed |
| `in-progress` | Messages are being sent |
| `completed` | All messages processed |
| `delivered` | Message delivered (per-message status) |
| `failed` | Send failed |

### Audience Types

| Type | Description |
| - | - |
| `customers` | Dynamic — uses customer filters (same filters as `/customers`) |
| `bookings` | Dynamic — uses booking filters (same filters as `/bookings`) |
| `community` | All members of a linked community |

### Template Variables

Templates support placeholder variables like `{first_name}`, `{last_name}`, etc. These are replaced per-recipient using the recipient model's `getVariableBindings()`. The template body is localized per customer's preferred locale.

***

**1. Create a Template**

```http theme={null}
POST {BASE_URL}/broadcast-templates?o={org_id}
```

```json theme={null}
{
  "data": {
    "type": "broadcast-templates",
    "attributes": {
      "name": "March Newsletter",
      "subject": "Hello {first_name}!",
      "body": "<p>Hi {first_name}, here's your monthly update...</p>",
      "body_short": "Hi {first_name}, check your monthly update.",
      "action_label": "View Details",
      "action_url": "https://example.com/updates",
      "channels": ["mail"],
      "is_template": false
    }
  }
}
```

| Attribute | Type | Description |
| - | - | - |
| `name` | string | Internal name |
| `subject` | string | Email subject (supports variables and i18n) |
| `body` | string | HTML email body (supports variables and i18n) |
| `body_short` | string | SMS body (supports variables and i18n) |
| `action_label` | string | CTA button text |
| `action_url` | string | CTA button URL |
| `channels` | string\[] | `["mail"]`, `["sms"]`, or `["mail", "sms"]` |
| `is_template` | boolean | `true` = reusable template; `false` = one-off |
| `binding_type` | string | Variable binding context |

For i18n, send localized versions via `subject_i18n`, `body_i18n`, etc. as objects keyed by locale.

### Includes

| Include | Description |
| - | - |
| `files` | Attached files |

### Filters

| Filter | Description |
| - | - |
| `filter[search]` | Name search |
| `filter[binding_type]` | Filter by binding type |
| `filter[is_template]` | `true` for reusable templates only |

**2. Create an Audience**

```http theme={null}
POST {BASE_URL}/broadcast-audiences?o={org_id}
```

**Dynamic audience (filtered customers)**

```json theme={null}
{
  "data": {
    "type": "broadcast-audiences",
    "attributes": {
      "name": "Active Customers 2026",
      "audience_type": "customers",
      "filters": {
        "search": "active"
      },
      "is_template": false
    }
  }
}
```

The `filters` object accepts the same filters available on the `/customers` endpoint (for `customers` type) or `/bookings` endpoint (for `bookings` type).

**Dynamic audience (filtered bookings)**

```json theme={null}
{
  "data": {
    "type": "broadcast-audiences",
    "attributes": {
      "name": "Upcoming Bookings",
      "audience_type": "bookings",
      "filters": {
        "status": "accepted",
        "date_from": "2026-04-01"
      }
    }
  }
}
```

| Attribute | Type | Required | Description |
| - | - | - | - |
| `name` | string | yes | Audience name |
| `audience_type` | string | yes | `customers`, `bookings`, or `community` |
| `filters` | object | no | Dynamic filter criteria |
| `is_template` | boolean | no | Reusable audience template |

### Includes

| Include | Description |
| - | - |
| `community` | Linked community (for `community` type) |

**3. Create a Campaign**

```http theme={null}
POST {BASE_URL}/broadcast-campaigns?o={org_id}
```

```json theme={null}
{
  "data": {
    "type": "broadcast-campaigns",
    "attributes": {
      "name": "March 2026 Newsletter"
    },
    "relationships": {
      "template": { "data": { "type": "broadcast-templates", "id": "{template_id}" } },
      "audience": { "data": { "type": "broadcast-audiences", "id": "{audience_id}" } }
    }
  }
}
```

| Attribute | Type | Required | Description |
| - | - | - | - |
| `name` | string | yes | Campaign name |
| `scheduled_at` | datetime | no | Schedule for future send |
| `recurring` | boolean | no | Enable recurring sends |
| `cron` | string | no | Cron expression (required when `recurring=true`). Format: `minute hour day month weekday` |

### Optional: Schedule or make recurring

```json theme={null}
{
  "data": {
    "type": "broadcast-campaigns",
    "attributes": {
      "name": "Weekly Reminder",
      "scheduled_at": "2026-04-01T09:00:00Z",
      "recurring": true,
      "cron": "0 9 * * 1"
    },
    "relationships": {
      "template": { "data": { "type": "broadcast-templates", "id": "{template_id}" } },
      "audience": { "data": { "type": "broadcast-audiences", "id": "{audience_id}" } }
    }
  }
}
```

**4. Preview Draft Messages**

Before sending, preview the generated messages (max 10):

```http theme={null}
GET {BASE_URL}/broadcast-campaigns/{id}/draft-messages?o={org_id}
```

Returns a collection of `broadcast-messages` with variable placeholders resolved per recipient, channels determined (invalid channels auto-removed if customer lacks email/mobile), and content localized.

**5. Start the Campaign**

```http theme={null}
GET {BASE_URL}/broadcast-campaigns/{id}/start?o={org_id}
```

Behavior depends on campaign config:

| Scenario | What happens |
| - | - |
| No `scheduled_at` | Sends immediately |
| `scheduled_at` in the future | Queues for scheduled delivery |
| `recurring=true` | Calculates next run from `cron`, sends at that time, then reschedules |

The campaign transitions through: `scheduled` → `pending` → `in-progress` → `completed`.

Progress is tracked via `sent_count`, `failed_count`, and `total_count` attributes. These update in real-time (a WebSocket event `broadcast-campaigns.updated` fires every 5 messages and on completion).

***

## Stop a Campaign

Reverts the campaign to `draft` and deletes all unsent (pending/draft) messages:

```http theme={null}
GET {BASE_URL}/broadcast-campaigns/{id}/stop?o={org_id}
```

***

## List Campaigns

```http theme={null}
GET {BASE_URL}/broadcast-campaigns?o={org_id}&include=template,audience
```

### Filters

| Filter | Description |
| - | - |
| `filter[search]` | Name search |

### Sorting

| Sort | Description |
| - | - |
| `name` | Campaign name |
| `created_at` | Creation date |

### Includes

| Include | Description |
| - | - |
| `template` | The message template |
| `template.files` | Template with file attachments |
| `audience` | The target audience |

***

## List Campaign Messages

```http theme={null}
GET {BASE_URL}/broadcast-campaigns/{id}/messages?o={org_id}&include=customer
```

### Message Attributes

| Attribute | Type | Description |
| - | - | - |
| `subject` | string | Resolved email subject |
| `body` | string | Resolved HTML body |
| `body_short` | string | Resolved SMS body |
| `action_label` | string | CTA text |
| `action_url` | string | CTA URL |
| `channels` | string\[] | Delivery channels for this recipient |
| `locale` | string | Recipient's locale |
| `email` | string | Recipient email |
| `mobile` | string | Recipient mobile |
| `status` | string | `draft`, `pending`, `completed`, `delivered`, `failed` |
| `created_at` | datetime | |

### Message Includes

| Include | Description |
| - | - |
| `campaign` | Parent campaign |
| `recipient` | The recipient (customer or booking) |
| `customer` | Related customer |
| `sent_email` | Tracked email record |

***

## Full Workflow

```text theme={null}
1. POST /broadcast-templates          → Create message content
2. POST /broadcast-audiences          → Define recipients
3. POST /broadcast-campaigns          → Create campaign linking template + audience
4. GET  /broadcast-campaigns/{id}/draft-messages → Preview
5. GET  /broadcast-campaigns/{id}/start          → Send
6. GET  /broadcast-campaigns/{id}/messages       → Monitor delivery
```

All endpoints include `?o={organization_id}` for tenant context.


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