Campaigns API
Auth: Clerk session.
CRUD
GET /v1/campaigns?cursor=&limit=50&status=draft
GET /v1/campaigns/:id
POST /v1/campaigns PATCH /v1/campaigns/:id DELETE /v1/campaigns/:id
// Create:
{
"name": "Welcome series",
"type": "broadcast",
"segment_id": "uuid",
"template_id": "uuid",
"provider_id": "uuid",
"channel": "email",
"schedule_at": null, // ISO if scheduled, else null = immediate
"goal_event": "deposit_confirmed", // optional
"goal_attribution_days": 7,
"goal_value_property": "amount_base"
}
Send
POST /v1/campaigns/:id/send
Triggers fan-out:
- Validates campaign is in
draft | scheduledstatus - Resolves segment + walks members in batches of 1000
- Filters self-excluded + frequency-capped + quiet-hours players
- Assigns variants (deterministic FNV-1a hash)
- Inserts
message_sendsrows + enqueues BullMQ jobs
{
"data": {
"campaign_id": "uuid",
"recipients_total": 3085,
"enqueued": 2914, // sent to BullMQ
"no_recipient": 33, // missing email/phone
"enqueued_at": "..."
}
}
Idempotent via Idempotency-Key header — replays return the same response without re-fanning out.
Cancel
POST /v1/campaigns/:id/cancel — drains in-flight; new fan-out blocks. Status → cancelled.