Skip to content

Broadcasts

A broadcast sends one template to an audience: a product announcement, a promotion, an incident notice.

POST /v1/broadcasts
{
  "name": "Autumn sale",
  "template": "autumn-sale",
  "category": "marketing",
  "channels": ["push", "email"],
  "audience": {"type": "filter", "condition": {"==": [{"var": "recipient.attributes.plan"}, "free"]}},
  "variables": {"discount": 20}
}

Audiences:

type
all Every recipient.
recipients external_ids: an explicit list (unknown ids are skipped).
filter condition: JSON Logic over {"recipient": …}.

A broadcast is created as a draft. POST /v1/broadcasts/{id}/start (optionally with {"at": "2026-11-01T09:00:00Z"}) schedules it.

How it runs

Recipients are processed in batches of 500, each in its own transaction, with a stored cursor. Every recipient passes through the regular policy — unsubscribes, caps and quiet hours apply — and every message carries a dedup key unique to the broadcast and recipient. Consequently:

  • Pause (/pause) takes effect after the current batch.
  • Resume (/resume) continues from the cursor.
  • Retries and crashes never double-send: a re-run batch finds the existing messages.
  • Cancel (/cancel) stops the broadcast and withdraws messages not sent yet — including ones waiting for quiet hours to end, even after the broadcast completed.

GET /v1/broadcasts/{id} reports progress in stats (targeted, queued, suppressed, failed…). Starting a scheduled broadcast again moves its start time. If a batch keeps failing until its retries run out, the broadcast becomes failed with the reason in error. Messages are listed with GET /v1/messages?broadcast_id=….