Broadcast Campaigns

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


Headers (every request)

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

draftscheduledpendingin-progresscompleted

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.


Step 1 — Create a Template

POST {BASE_URL}/broadcast-templates?o={org_id}
{
  "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 2 — Create an Audience

POST {BASE_URL}/broadcast-audiences?o={org_id}

Dynamic audience (filtered customers)

{
  "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)

{
  "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)

Step 3 — Create a Campaign

POST {BASE_URL}/broadcast-campaigns?o={org_id}
{
  "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

{
  "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 4 — Preview Draft Messages

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

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 5 — Start the Campaign

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: scheduledpendingin-progresscompleted.

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:

GET {BASE_URL}/broadcast-campaigns/{id}/stop?o={org_id}

List Campaigns

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

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

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.