Preflight review of a campaign audience
An audience is approved for launch by looking at it, not by hoping the send resolves the way you drew it up.POST /api/v1/campaigns/audience/preview is the instrument for that look: feed it the same audience shape the campaign carries and it returns the gross match count, a 10-contact sample, and — when you pass a channel — the net projection the launch will actually attempt. This page walks one campaign through that review: a summer-sale SMS blast built on a static list.
The preview is a decide step, not a discover step. Send a campaign end-to-end covers the full lifecycle where this review sits; Outbound compliance pre-flight checklist covers the gate chain (wallet, opt-out sync, quiet hours, frequency caps) that runs after the audience itself is sound. Consensus between those three passes is what clears launch.
1. Why preflight beats post-launch debugging
Post-launch, a bad audience surfaces as absence: rows that never appear in the send log, a stat total that visibly under-delivers, or a suppression cohort you learn about once the ledger has already counted them. At that point the campaign state is real — credits committed (or quietly not spent), and the debugging happens against launch artifacts whose zeroes are ambiguous (was the row suppressed, opted out, address-less, or capped?). Preflight, the same defects are cheap and disambiguated. The preview is read-only: it mutates nothing, touches no wallet, enqueues nothing. Veto a bad list and the cost is an edited CSV and a re-check; veto three hours later and the cost is a launch artifact to explain. Run the review while the campaign is still a draft and the cost of a wrong audience stays at zero.2. Run the preview and read the net projection
Post the same audience shape the campaign carries. For the summer-sale blast, the audience is a static list namedlist_summerVIPs and the channel is SMS:
matching_count is the gross match — the audience shape on its own, before any channel exclusions. Pass channel and the response adds the cohort block, the net projection that partitions the gross into the cohorts the launch will see:
Four reading rules:
- Read
net, notmatching_count, as the headcount. A healthy summer-sale audience yields most of its gross innetand a smallsuppressed/opted_outcohort. Ifnetis materially below gross, the difference is assigned to specific cohorts in the response — that assignment is what remediates. unreachableshould trend to zero on a phone channel for a well-kept list. Non-zerounreachablemeans missing contact data, not a compliance block.- Expect
suppressed+opted_outto be small but non-zero. A list that has ever been sent will carry some excludes; a perfectly clean pair is the anomaly to double-check, not the goal. frequency_cappedis advisory, not a gate. It is the estimate of sends your own configured caps will hold back — it never blocks launch, and a large value means “your cap policy is about to shave this many,” nothing more.
suppressed / opted_out / unreachable / deliverable math reappears in the dry-run (audience.* buckets), so the preview’s reading carries forward verbatim into the end-to-end gate — the reconciliation stays exact.
3. The five-minute cache in the render loop
Results are cached for five minutes on a hash of the criteria —cached: true on a hit. Two consequences for the wizard render loop:
- Re-rendering the same drawer is free. Repeated preview calls with the same audience shape replay the cached projection instead of re-evaluating, so the preview card can refresh on every keystroke without paying for a resolution pass.
- Edits break the cache. Change the criteria — a different
audience_id, a regeneratedcsv_recipients, an addedcontact_idsmember — and the hash changes, so the next call re-evaluates live. If you’re troubleshooting and a fix you just made doesn’t move the numbers, confirm the criteria actually changed before assuming the cache is stale; identical criteria within the five-minute window is a cache hit by design.
4. The matching_count = 0 fix ladder
A zero gross match means the audience shape itself resolved to nothing — before any suppression, opt-out, or address math ran. Work the ladder top to bottom; each rung names one cause and its one fix.
Rung membership matters: a zero
matching_count is never a compliance condition. Suppression and opt-outs reduce net from a healthy gross, they never zero matching_count — so a zero is a roster problem with one of the five fixes above, not a remediation of the block list.
5. Where this sits in the launch path
The audience review is the first gate, not the only one. Two hand-offs close the path:- Back into Send a campaign end-to-end §3 — that guide runs this preview as step 3 of the lifecycle and immediately consumes the cohort counts in the dry-run; return there once the audience reads clean.
- Across to Outbound compliance pre-flight checklist — a healthy audience still walks the admission chain; the checklist then owns the wallet posture, out-of-band opt-out sync, quiet hours, and frequency-cap posture this page deliberately left aside.