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

# Batch DNC Pre-Flight Scrubbing

> Scrub a whole campaign audience against Do-Not-Call sources before you send — the bulk scrub endpoint, the org opt-in gate, feed-sync signals, and how to handle flagged numbers.

# Batch DNC Pre-Flight Scrubbing

A pre-flight scrub answers one question before a campaign leaves the
building: **which numbers on this list would the send path's Do-Not-Call
gate block anyway?** Run it on a cold list or a campaign audience, remove
the flagged numbers, and only then launch.

Orbit gives you two reads of the same DNC chain the send path enforces:

* `GET /compliance/dnc/check` — one number at a time.
* `POST /compliance/dnc/scrub` — a batch of up to 500 numbers per request,
  returning a per-number verdict plus summary counts.

The batch endpoint is what you want before a dialer or SMS campaign. This
guide covers the full workflow: why pre-flight is worth the trouble, how
the org gate works, how to run the scrub, and how to handle what comes
back. For sources, feed wiring, and freshness fields, see
[DNC Scrubbing](/compliance/dnc-scrub); for the send-time gates, see
[Send Gates](/compliance/send-gates).

All endpoints below are rooted at
`https://api.orbit.devotel.io/api/v1/compliance`.

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

***

## Why pre-flight

DNC obligations are your responsibility as the sender, and the exposure
is per call. In the US, dialing or texting a number on the FTC National
Registry in violation of the Telemarketing Sales Rule is a per-call
violation under 16 CFR § 310.4(b)(1)(iii)(B), with civil penalties in
the tens of thousands of dollars per violating call. State registries
and the TCR Universal Opt-Out list layer on top of the federal register
— a federal-only scrub is not complete for US traffic. Outside the US,
equivalent national registers apply: UK TPS/CTPS, Australia's Do Not
Call Register, India's NDNC.

The send path already blocks numbers that fail the chain, so a pre-flight
scrub is not about whether blocked numbers go out — they won't. It is
about three practical things:

1. **You decide *what* to do with flagged numbers.** A blocked send is a
   dropped message; a pre-flight verdict is a list you can route to
   suppression, remove from a segment, or review for consent before the
   campaign launches.
2. **You find out before launch, not after.** A campaign that drops 40%
   of its audience at the send gate is a wasted launch and a confused
   reporting dashboard.
3. **You get a defensible record.** Every scrub is audit-logged with
   aggregate counts, so a compliance review can show the pre-flight ran
   before the campaign went out.

***

## The two read surfaces

**Single number — `GET /compliance/dnc/check`.** One E.164 number per
request, rate-limited as a standard authenticated read (120 requests per
minute per organization). Use it for ad-hoc lookups — an operator checking
a single contact before a manual dial.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/compliance/dnc/check?phone=%2B14155550101" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

**Batch — `POST /compliance/dnc/scrub`.** Up to **500 numbers per
request** (duplicates are de-duplicated before scrubbing), rate-limited
to **10 requests per minute per organization**. Page larger audiences in
500-number chunks. That rate combination is sized so a mid-size campaign
pre-flight never trips it; a script hammering the endpoint in a tight
loop will hit HTTP 429 instead of a verdict.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/compliance/dnc/scrub" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phones": ["+14155550101", "+442071838750"] }'
```

Both endpoints accept an optional `country` parameter (ISO 3166-1
alpha-2, e.g. `GB`, `AU`, `IN`) that scopes the international
national-registry lookup to one jurisdiction — a UK campaign is never
flagged on an unrelated country's record.

Every per-number result carries the same shape:

```json theme={null}
{
  "results": [
    {
      "phone": "+14155550101",
      "on_dnc": true,
      "source": "federal_dnc",
      "jurisdictions": ["US Federal DNC"]
    }
  ]
}
```

The `source` tells you which layer flagged the number:

| `source`      | Meaning                                                                                                |
| ------------- | ------------------------------------------------------------------------------------------------------ |
| `federal_dnc` | US FTC National Registry                                                                               |
| `state_dnc`   | A US state registry                                                                                    |
| `tcr`         | TCR Universal Opt-Out                                                                                  |
| `intl_dnc`    | An international national registry (UK TPS/CTPS, Australia DNC, India NDNC)                            |
| `suppression` | Your own suppression layer — contact DNC flag, DNC list, suppression list, or recorded consent opt-out |
| `none`        | No DNC source flagged the number                                                                       |

`jurisdictions` lists every matching origin, so a number on both the
federal registry and a state registry surfaces both.

Prioritize by `source`: registry hits (`federal_dnc`, `state_dnc`, `tcr`,
`intl_dnc`) are registry obligations — remove them from the audience.
A `suppression` hit is your own recipient telling you to stop — it also
belongs on your suppression list if it is not there already (see below).

***

## The acknowledgment gate, in order

Both DNC endpoints are gated the same way. The gate resolves in this
order — first true term wins:

1. **A platform feed is synced.** Once Orbit has ingested a US
   federal/state/TCR snapshot or an international national-registry
   snapshot, the endpoints serve directly (`federal_feeds_synced: true`
   / `intl_feeds_synced: true` on every response). The gate is retired —
   no org action needed, ever again for that feed class.
2. **Your organization opted in.** While no feed is synced, the
   endpoints are held behind your organization's Do-Not-Call opt-in
   (the `dnc_sync_enabled` setting in your organization's compliance
   settings — it is listed, with its default, on the
   [posture map](/compliance/posture-overview)). The default is **off**.

Until one of those is true, every call to either endpoint returns HTTP
403:

```json theme={null}
{
  "error": {
    "code": "DNC_SYNC_NOT_ENABLED",
    "message": "This bulk DNC scrub requires your organization's Do-Not-Call opt-in to be enabled. Turn it on in your organization's compliance settings, then retry.",
    "status": 403,
    "details": { "federal_feeds_synced": false }
  }
}
```

The acknowledgment you are making by opting in: while no platform feed
is synced, Orbit is not scrubbing against the federal register for you —
so a number only on the FTC list reads back as `on_dnc: false`. The
opt-in does not weaken any enforcement: your own DNC, suppression, and
consent opt-out enforcement on outbound send paths runs regardless. One
flip enables both endpoints.

<Note>
  **Always read `federal_feeds_synced` and `intl_feeds_synced` on every
  response.** They distinguish "the number is clear" from "the number is
  clear **and** a registry scrub backed that answer." A clear verdict
  with `federal_feeds_synced: false` only means your own lists,
  suppression, consent, and the platform list did not flag the number.
</Note>

**Checking without running a check.** `GET /compliance/dnc/availability`
reports the gate state without scrubbing anything — the dashboard's
list-scrub card uses it to grey out or enable the scrub action up front:

```json theme={null}
{
  "available": true,
  "org_opt_in": true,
  "federal_feeds_synced": false,
  "intl_feeds_synced": false
}
```

`available` is the exact three-term gate above. When it is `false`, the
per-org opt-in is the only self-service remedy.

***

## Campaign-run workflow

Put the scrub between building the audience and launching the campaign:

1. **Build your segment** with the audiences/CDP tools as usual.
2. **Check the gate** — `GET /compliance/dnc/availability`. If
   `available` is false, enable the org opt-in (above) first.
3. **Scrub the segment.** Page the audience into 500-number chunks and
   POST each chunk to `/compliance/dnc/scrub`.
4. **Split on the verdicts.** Route every `on_dnc: true` row out of the
   send audience:
   * Registry sources (`federal_dnc`, `state_dnc`, `tcr`, `intl_dnc`) —
     remove the number from the campaign. Do not attempt to send.
   * `suppression` — add the number to your suppression list (or move it
     across contacts lists) so future audiences exclude it up front; see
     [Opt-Out & Suppression Lists](/compliance/opt-out-suppression).
5. **Launch** against the scrubbed audience — see
   [Outbound dialer campaigns](/guides/outbound-dialer-campaign) and
   [Campaign end-to-end](/guides/campaign-end-to-end).
6. **Re-scrub stale audiences.** National registries update on a
   business-day cadence and recipients opt out continuously, so re-run
   the scrub before each send for an audience you have held for more
   than a couple of days. The verdict you ran at audience-build time
   ages out as registries and consent records move.

The scrub never blocks what you keep: it is read-only. The gate that
prevents a violating send is the send path, which re-checks the same
chain at dispatch. The pre-flight is how you stop feeding the send path
numbers it will only drop.

***

## How it interacts with suppression and the preference center

The DNC chain treats registry obligations and your own suppression layer
as one answer, but the `source` field keeps them apart:

* **Registry sources** (`federal_dnc`, `state_dnc`, `tcr`, `intl_dnc`)
  come from synced national-register data. You manage that by keeping
  your audiences away from flagged numbers — you cannot remove someone
  from the FTC registry.
* **`suppression`** is the recipient's own signal reaching you: a STOP
  reply, a contact flagged DNC, an entry on a DNC or suppression list,
  a recorded consent opt-out, or a preference-center choice. These you
  *can* remedy — a recipient who opts back in through the
  [preference center](/compliance/send-gates#preference-center) reverses
  the opt-out, and their number reads back clear on the next scrub.

Feedback loop worth building: when a scrub flags a number on `source:
"suppression"` that you did not expect, check the contact's consent
record before you keep suppressing them — and when a recipient re-consents,
re-scrub the segment before relying on the older verdict. See
[Opt-Out & Suppression Lists](/compliance/opt-out-suppression) and
[Consent Management](/compliance/consent-management).

***

## Troubleshooting

**HTTP 403 `DNC_SYNC_NOT_ENABLED` on every call.** The gate (above) is
closed for your organization: no platform feed is synced for the class
you are querying and your org opt-in is off. Enable the Do-Not-Call
opt-in in your organization's compliance settings and retry. Confirm the
state with `GET /compliance/dnc/availability` — if `available` is still
false after you flipped the setting, check you saved the organization
settings change.

**Everything reads back clear — but you suspect that is wrong.** Check
`federal_feeds_synced` and `intl_feeds_synced` on the response. If both
are `false`, no registry scrub backed the verdicts: a number only on the
FTC register (or a UK/AU/IN register) reads back as `on_dnc: false`
until the platform syncs the matching feed. The scrub is still telling
you your own suppression story; it is not telling you the registries'
story. Do not treat clear verdicts as a federal safe harbor until the
matching feed flag is `true`.

**HTTP 429 on bulk scrubs.** The batch endpoint allows 10 requests per
minute per organization. You are paging too aggressively — back off to
one chunk every \~6 seconds, or chunk at the full 500 numbers per request
so fewer requests carry the same audience. A 429 never consumes a
request's verdicts: retry the same chunk after the window resets.

**Numbers you removed get flagged again on the next scrub.** Expected —
the verdict describes the number's registry state, not your
list-building. Route repeated `suppression` hits onto your suppression
list once, and let segment membership keep them out instead of
re-discovering them each campaign.

***

See also:

* [DNC Scrubbing](/compliance/dnc-scrub) — sources, feed wiring,
  freshness timestamps, and the fail-open caveat.
* [Send Gates](/compliance/send-gates) — the quiet-hours / DNC / RND /
  RMD gates that run at send time.
* [Opt-Out & Suppression Lists](/compliance/opt-out-suppression) — the
  layer the `suppression` source reads.
* [Your Tenant Compliance Posture](/compliance/posture-overview) — the
  toggle map, including the Do-Not-Call opt-in row.
* [API Reference → Compliance](/api-reference/endpoints/compliance) —
  full request/response schemas.
