GET /v1/campaign/{id}/status— latest run’s progress, polled while live.GET /v1/campaign/{id}/analytics— campaign-wide aggregate rollup.GET /v1/campaign/{id}/runs/{run_id}/snapshot— frozen execution snapshot (content, sender, channel, cost, recipients).
Status translation. Every run-status field in the responses below translates the engine’s internal
claimed sentinel to running. As an API consumer you will never see claimed — treat running as the in-flight state.1. Live status — for polling loops
Dashboard. The campaign view updates automatically as batches complete. You see the current batch / total batches and live counters for sent, failed, skipped, and delivered. Developer API.- Returns the latest run’s execution status plus batch progress counters.
- Mirrors the user-facing
/campaigns/{id}/statusendpoint shape — the same fields whether you call the developer or the user surface. - This is the endpoint to poll while a campaign is active.
Success response (run exists)
Response when no run yet
NO_RUN is a sentinel, not a run status. When the campaign has zero runs, the endpoint returns the literal string "NO_RUN" in status. This value is not part of the normal run-status enum (scheduled, running, completed, failed, cancelled). Check for status === "NO_RUN" before treating status as a run state.Field reference
Errors
Code samples
Polling cadence
A reasonable polling rhythm.
- 5–15 seconds is fine while
statusisscheduledorrunning. - Stop polling as soon as
statusflips tocompleted,failed, orcancelled— counters are terminal at that point. - Then call
/analyticsonce to grab the final rollup. Do not keep polling/statusafter the terminal transition.
2. Aggregate analytics
Dashboard. The campaign’s “Analytics” tab shows total recipients, sent / delivered / failed / skipped counts, plus delivery and failure rates as percentages. Developer API.- Returns campaign-wide aggregate analytics across all runs (delivery rate, failure rate, counts, timing).
- When no analytics row has been written yet (e.g. a campaign created but never run, or polled in the first seconds after launch), the endpoint returns a zeroed-out payload with
nullrate fields. HTTP status is still 200 — checkdelivery_rate !== nullto know whether percentages are renderable.
Success response (populated)
Field reference
Analytics rows are written after the first batch completes. There is a small delay (seconds) between a campaign starting and analytics being non-zero. Do not hit
/analytics immediately after /launch and expect populated counters — poll /status until it reports activity, or wait for the terminal transition.Errors
Code samples
3. Run detail — composite payload
Dashboard. Click any run in the campaign’s “Runs” tab to open a detail modal — the run row, channel + provider + sender metadata, per-batch progress, and a per-recipient list with live message status. Developer API.Batch fields
Recipient fields
recipients[] is capped at 500 rows per response. When more exist, the API sets recipients_truncated: true and clips the array. Use recipient_summary for the aggregate counts — do not derive totals by counting recipients[]. If you need every recipient for a large run, page through the run via the dashboard or contact Briq for a bulk export.message_status may diverge from recipient_status. The message status is the live truth pulled from the messages table; recipient_status is a slightly stale projection rolled up onto the per-run recipient row. Prefer message_status when you display the current state of a single message.Errors
Code samples
Run snapshot (read-only, PII-gated)
Developer API.- Returns the engine’s
execution_snapshot— the frozen, point-in-time state used to dispatch this run: content, sender, channel, provider, cost, recipient count, and optionally the recipient list. - Query param
?include_recipients=trueopts into returning the recipient phone-number list. Stripped by default.
Snapshot may be partial before dispatch. For a run still in
ready status (queued but not yet picked up by the worker), the engine may not have frozen the full snapshot yet — some fields may be null. Re-read after the run transitions to running or later.Success response (default, without recipients)
Success response (with ?include_recipients=true)
The same payload, plus a recipients array of phone numbers (E.164 digits):
Errors
Code samples
What happens after launch
A short narrative of the system behaviour that drives the numbers you see above. Useful if you are building a poller and want to understand the timing.- Pickup (≤ 30 seconds). A separate worker microservice runs a cron query of
campaign_runs WHERE status = 'scheduled' AND scheduled_at <= now(). It claims the row by flipping its status to the internalclaimedsentinel. The API translatesclaimedtorunningin every response, so you will only ever seerunning. - Batches of 1000. The engine partitions the run’s recipients into batches of up to 1000 and writes a
campaign_batchesrow per partition. Each batch is dispatched through the campaign’s channel + provider; per-recipient rows land incampaign_recipientsandmessages. - Counters roll up. As batches complete,
sent_count,failed_count,skipped_count, anddelivered_counton the run row are incremented. The parent campaign’s aggregate counters are recomputed off the run’s totals — this is what populates the next/statusand/analyticsresponse. - Terminal state and analytics. When all batches finish, the run flips to
completed(orfailed). The engine then writes acampaign_analyticsrow — at this point/analyticsreturns its populated payload. - Optional refund. If a run failed partway through, or the campaign was cancelled, the engine refunds the unsent portion of the credit reservation. Synchronous on
/cancel; reconciler-driven for other failure paths.
Practical cadence
A 4-bullet recap of what to call when:- During
scheduled/running: poll/statusevery 5–15 seconds. - On terminal transition (
completed,failed, orcancelled): stop polling, call/analyticsonce for the final rollup. - For a run-level UI (drilldown modal, per-recipient list): call
/runs/{run_id}for the composite payload. Use the snapshot endpoint only when you need the frozen dispatch state. - Prefer the dashboard for ad-hoc inspection. Build a poller only when you need automation — e.g. piping data into your own analytics, alerting on failure rates, or driving a custom operator console.
Related guides
- Campaigns overview — concepts, lifecycle, and the full endpoint map.
- Launch and control — scheduling, cancelling, and the run lifecycle that this page reports on.