> ## 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.

***

<Steps>
  <Step title="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 |
  </Step>

  <Step title="Create an Audience">
    ```http theme={null}
    POST {BASE_URL}/broadcast-audiences?o={org_id}
    ```

    <Tabs>
      <Tab title="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).
      </Tab>

      <Tab title="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"
              }
            }
          }
        }
        ```
      </Tab>
    </Tabs>

    | 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) |
  </Step>

  <Step title="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}" } }
        }
      }
    }
    ```
  </Step>

  <Step title="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.
  </Step>

  <Step title="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).
  </Step>
</Steps>

***

## 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.
