Broadcasts
One-off marketing campaigns to your contacts. Race-safe state machine with mid-send cancellation, send-time unsubscribe re-check, and live progress polling.
Broadcasts let you send a one-off email to all contacts (or contacts matching a tag) without setting up a recurring newsletter. They are managed via the dashboard, but the worker exposes a JWT-authenticated API for cancel and send operations. State machine: `draft` → `sending` → `sent` | `failed` | `cancelled` Production-hardening features: • **Race-safe transitions** — every state-changing UPDATE includes the expected current status in the WHERE clause (e.g. `WHERE status='draft'`), so a concurrent cancel that lands in the gap between page activations always wins. • **Send-time unsubscribe re-check** — before each individual SES handoff, the worker re-reads the contact's `unsubscribed` field. Contacts who unsubscribed after the initial page query but before queue insertion are skipped. • **Audit log** — every state change is appended to `broadcasts.audit_log` (jsonb) with the action, timestamp, and details for debugging. • **Cancel mid-send** — the fan-out loop checks status before each page of contacts, stopping cleanly. Already-queued BullMQ jobs still deliver (we don't reach into the queue), but no further pages fan out. • **Live progress polling** — the dashboard polls every 3 seconds while a broadcast is `sending`, showing a live `sent / total` count and progress bar. ## Throughput Broadcast fan-out shares the same SES rate limit as everything else: **AWS SES caps the account at 14 emails/sec** and the Posthawk worker rate-limits dispatch to **12 emails/sec** to leave headroom. So a broadcast to 1,000 contacts takes ~83 seconds to fully ship, 10,000 contacts ~14 minutes. The dashboard's live progress bar reflects the actual drain rate. API endpoints (JWT-authenticated, called from the dashboard):
POST
/broadcasts/:id/sendJWTTrigger a broadcast send. Race-safely transitions from draft → sending and starts fanning out contacts. Returns when fan-out completes (or is cancelled).
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Broadcast UUID |
Response
json
{
"success": true,
"queued": 1247,
"message": "Broadcast sent to 1247 recipients"
}POST
/broadcasts/:id/cancelJWTCancel an in-flight or draft broadcast. Race-safe: only flips a row in draft or sending state. Already-queued BullMQ jobs still deliver, but no further pages of contacts are fanned out.
| Parameter | Type | In | Description |
|---|---|---|---|
idrequired | string | path | Broadcast UUID |
Response
json
{
"success": true,
"status": "cancelled",
"alreadyQueued": 423,
"message": "Cancelled — 423 already-queued emails will still be delivered"
}