List-Hygiene Projection: DNC Suppression on the Pre-Flight Preview
The campaign dry-run answers “how many recipients will actually receive this send, and what will it cost?” before you launch. Until the list-hygiene projection landed, one dimension was missing from that answer: the Do-Not-Call gate the send path enforces. The dry-run already shows reachability (suppression, opt-out, missing address) and a spend projection — but a number on a DNC or suppression list still counted as “deliverable,” was dropped at dispatch, and the spend projection included it. The list hygiene section closes that gap: it samples your deliverable recipients through the same DNC/suppression chain the send path enforces, projects the hit ratio onto the whole cohort, and turns the result into an avoided-spend estimate — all before the campaign launches.The dimension the dry-run used to miss
Launch a phone-shaped campaign (SMS, MMS, voice, RCS, Viber, WhatsApp, and the other E.164 channels) and the send path checks every recipient against your DNC sources — the contact DNC flag, your DNC and suppression lists, recorded consent opt-outs, and the synced national registries — before anything goes out. A recipient matched on any of them is silently skipped. Before this projection existed, that skip was invisible at planning time:- The dry-run resolved
audience.suppressed,audience.opted_out, andaudience.unreachable(no deliverable address for the channel) and netted all three out ofaudience.deliverable— so the preview already discounted the recipients it could see wouldn’t receive the send. - DNC-listed numbers never got that treatment. A number listed on your DNC list or a synced national registry counted as deliverable, the cost projection charged for it, and the send gate dropped it at dispatch without ever telling you the drop was coming.
- The result was a discovery loop on every launch: launch the campaign, watch recipients vanish at the send gate, find out after the fact how much spend the list would have wasted.
How the projection is computed
POST /api/v1/campaigns/:id/dry-run evaluates both the DB-bound
sampling step and the pure ratio math in one response:
- Sample the deliverable cohort. The dry-run samples up to 200 phone numbers from the campaign’s audience (list, segment, CSV roster, or the contact book), ordered by contact ID — the same deterministic bounded-sample strategy the quiet-hours projection uses.
- Run the same DNC chain the send gate enforces. Each sampled number goes through the same batch DNC/suppression check used at dispatch time: contact DNC flags, tenant DNC lists, cross-channel STOP suppression lists, recorded consent opt-outs, plus the platform-level federal/state/TCR and international registry feeds.
- Project the ratio onto the cohort. The blocked ratio observed in
the sample (
blocked_estimate = sample_blocked / sample_size × deliverable, rounded, and capped at the deliverable count) is extrapolated onto the full deliverable cohort. A clean sample exactly reports zero, never a phantom hit. - Convert to avoided spend. The projected block count is multiplied
by the same per-message unit price the dry-run’s cost estimate is
built with — the projection never invents a second rate — and
formatted as a
"$X.XX USD"savings figure.
checked: false and the preview carries a warning rather than fabricating a
zero.
Read-only throughout: the projection samples data the send path already
consults and writes nothing. It never dispatches a message, touches a
carrier, or charges anything — it is pure bookkeeping over the
compliance sources you own as a tenant.
Reading the response
The block appears in the dry-run response underlist_hygiene:
dnc.checked— whether the DNC sample actually ran.falsemeans the dimension was not evaluated (email-only channel, no channel configured, or the probe failed) — never read it as “checked and clear.” When it isfalse, treat DNC exposure as unknown.dnc.sample_size/dnc.sample_blocked— the raw sample stats. A highsample_blocked / sample_sizeratio on a small cohort is your sign to run the full batch scrub for per-number verdicts instead of relying on the projection.dnc.blocked_estimate— the projected count of the full deliverable cohort the send gate will drop.estimated_savings_cents/formatted_savings_usd— the spend that would have been wasted on recipients the send gate would have skipped anyway.nullwhen the dry-run could not resolve a unit price.
checked is
false, the panel shows nothing — a preview that could not run the
check never claims the list is clean.
The dry-run also folds the finding into its warnings array, e.g.
“Approximately 359 of 11,960 deliverable recipients are on a Do-Not-Call
list and will be skipped at send time — an estimated $4.66 USD in
avoided spend.” Warnings are informational, so they do not block
ready_to_launch — the point is to surface the skew before launch, not
to gate the send.
How it contrasts with the other dry-run projections
Three pre-flight projections now read off the same resolved audience:
All three fold into one audience-resolution pass, so the counts stay
consistent. The differences to keep straight:
- Reachability is exact, samples are estimates. Suppression /
opt-out / addressability counts run over the full audience, so
deliverableis exact. The DNC and quiet-hours projections sample — a bounded trade-off that keeps a 100K-recipient preview cheap. Use sampling for planning, and run the endpoint-level index checks below for per-number answers before launch. - DNC skips are avoided spend; quiet-hours skips are deferred spend. A DNC-blocked recipient never receives this send (the check is read-only — the number is not added to or removed from any list). A quiet-hours recipient is re-queued for the next allowed window.
- Warnings are informational. None of the three block
ready_to_launch— they exist so a skewed list surprises you before dispatch, not after.
Tenant-owned bookkeeping only
The projection is a read over your own compliance data expressed in planning numbers:- It never contacts a carrier, and it never sends. The same invariant that governs the whole campaign preview applies here — no outbound traffic is placed to compute it.
- It writes nothing. Sampling is read-only; the scrub verdicts consult your DNC lists, suppression lists, consent records, and synced national registries without mutating them.
- The DNC checks it runs are the same tenant-scoped sources the send gate enforces — there is no global country or registry gate layered in. What you can do with the answer (route flagged numbers to suppression, remove them from the segment, re-check consent) is the workflow covered in Batch DNC Pre-Flight Scrubbing.
See also:
- Campaign end-to-end — the launch flow the dry-run preview sits inside.
- Batch DNC Pre-Flight Scrubbing — the endpoint-level per-number scrub the sample projection complements.
- DNC Scrubbing — sources, feed wiring, and freshness fields for the chain the sample consults.
- Send Gates — quiet hours, DNC, RND, and RMD gates at send time.
- Campaign quiet hours — the sibling sample-based projection.