Skip to main content
Once a campaign launches, you can watch its progress in real time and inspect the final delivery breakdown when it completes. The Briq dashboard shows all of this on the campaign view — counters, per-batch status, recipient list, delivery rate. The developer API exposes the same data through three read-only endpoints.
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}/status endpoint 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 status is scheduled or running.
  • Stop polling as soon as status flips to completed, failed, or cancelled — counters are terminal at that point.
  • Then call /analytics once to grab the final rollup. Do not keep polling /status after 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 null rate fields. HTTP status is still 200 — check delivery_rate !== null to know whether percentages are renderable.

Success response (populated)

Field reference

Rates are 0.0–1.0 floats — multiply by 100 for display. A delivery_rate of 0.9398 is 93.98%, not 0.94%. Never display the raw value as a percentage. Always check for null before rendering.
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.
Returns the composite “run detail” payload — a single round-trip that gives you everything for a run-detail UI:

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=true opts into returning the recipient phone-number list. Stripped by default.
?include_recipients=true returns PII. The recipient list contains phone numbers. Never call this variant from end-user-facing surfaces (e.g. a customer-supportable web app, an unauthenticated dashboard view). Treat it as a server-to-server, admin-tier read. The caller is responsible for handling returned phone numbers under their own compliance regime.
cost is in service units, not currency. total_cost is denominated in currency_unitssms_parts, voice minutes, etc. — matching the units convention used by /validate. Do not display this value as money. Convert to your billing currency client-side if you need to show a price.
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.
  1. 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 internal claimed sentinel. The API translates claimed to running in every response, so you will only ever see running.
  2. Batches of 1000. The engine partitions the run’s recipients into batches of up to 1000 and writes a campaign_batches row per partition. Each batch is dispatched through the campaign’s channel + provider; per-recipient rows land in campaign_recipients and messages.
  3. Counters roll up. As batches complete, sent_count, failed_count, skipped_count, and delivered_count on the run row are incremented. The parent campaign’s aggregate counters are recomputed off the run’s totals — this is what populates the next /status and /analytics response.
  4. Terminal state and analytics. When all batches finish, the run flips to completed (or failed). The engine then writes a campaign_analytics row — at this point /analytics returns its populated payload.
  5. 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 /status every 5–15 seconds.
  • On terminal transition (completed, failed, or cancelled): stop polling, call /analytics once 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.