> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# List-Hygiene Projection: DNC Suppression on the Pre-Flight Preview

> Read the dry-run's list-hygiene block — the projection that tells you how many of your deliverable recipients the send path's Do-Not-Call gate will actually drop, and what that spend would have wasted.

# 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.

<Warning>
  This page describes Orbit's platform controls. It is **not legal
  advice.** Whether contacting a given number is restricted depends on
  your jurisdiction, your recipients, and what you send — confirm with
  qualified counsel.
</Warning>

***

## 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:

1. The dry-run resolved `audience.suppressed`, `audience.opted_out`, and
   `audience.unreachable` (no deliverable address for the channel) and
   netted all three out of `audience.deliverable` — so the preview
   already discounted the recipients it could see wouldn't receive the
   send.
2. 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.
3. 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.

The list-hygiene projection points the same DNC chain at a bounded
sample of the deliverable cohort during the dry-run itself, so the
operator sees "the send gate will skip roughly N of these recipients,
and scrubbing them avoids roughly \$X of spend" before launch. A bulk
[single-number or 500-at-a-time pre-flight scrub](/guides/dnc-preflight-scrub)
is still available when you want per-number verdicts; the projection is
for the campaign preview where a full scrub on every preview refresh
would be wasteful.

***

## 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:

1. **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](/guides/campaign-send-window-quiet-hours) uses.
2. **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.
3. **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.
4. **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.

Only phone-shaped channels are projected: DNC has no meaning for email,
so an email-only channel reports the DNC check as not run. A sampling
or pricing failure degrades the same way — the probe reports `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 under `list_hygiene`:

```json theme={null}
{
  "audience": {
    "total_in_audience": 12480,
    "suppressed": 320,
    "opted_out": 140,
    "unreachable": 60,
    "held_out": 0,
    "deliverable": 11960
  },
  "list_hygiene": {
    "dnc": {
      "checked": true,
      "sample_size": 200,
      "sample_blocked": 6,
      "blocked_estimate": 359
    },
    "estimated_savings_cents": 466,
    "formatted_savings_usd": "$4.66 USD"
  }
}
```

* `dnc.checked` — whether the DNC sample actually ran. `false` means the
  dimension was **not evaluated** (email-only channel, no channel
  configured, or the probe failed) — never read it as "checked and
  clear." When it is `false`, treat DNC exposure as unknown.
* `dnc.sample_size` / `dnc.sample_blocked` — the raw sample stats. A
  high `sample_blocked / sample_size` ratio on a small cohort is your
  sign to run the [full batch scrub](/guides/dnc-preflight-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. `null` when the dry-run could not resolve a unit price.

In the dashboard, the launch preview renders the same block as a
"List hygiene" panel: "DNC suppressed N (\~\$X saved)" when the sample
found hits, or a clear sample acknowledgement ("no Do-Not-Call matches
in the 200-recipient sample") when it found none. When `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:

| Projection                         | What it estimates                                                                                             | Data it consults                                                              |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Reachability** (audience counts) | Exact sub-counts: suppressed, opted-out, no-deliverable-address — netted out of `deliverable`                 | Contact suppression, consent, channel addressability over the full audience   |
| **Quiet hours**                    | How many deliverable recipients fall inside the restricted window now, and when the next allowed window opens | Your tenant's quiet-hours policy, sample-based over up to 200 recipients      |
| **List hygiene (DNC)**             | How many deliverable recipients the DNC gate will drop, plus avoided spend                                    | The full DNC/suppression source chain, sample-based over up to 200 recipients |

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
  `deliverable` is 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](/guides/dnc-preflight-scrub).

***

See also:

* [Campaign end-to-end](/guides/campaign-end-to-end) — the launch flow
  the dry-run preview sits inside.
* [Batch DNC Pre-Flight Scrubbing](/guides/dnc-preflight-scrub) — the
  endpoint-level per-number scrub the sample projection complements.
* [DNC Scrubbing](/compliance/dnc-scrub) — sources, feed wiring, and
  freshness fields for the chain the sample consults.
* [Send Gates](/compliance/send-gates) — quiet hours, DNC, RND, and RMD
  gates at send time.
* [Campaign quiet hours](/guides/campaign-send-window-quiet-hours) —
  the sibling sample-based projection.
