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

# DNC Scrubbing: Sources, Freshness, and the Check Endpoint

> Configure a complete Do-Not-Call posture — enable the org opt-in, understand where the sync sources come from, read the check endpoint's source and freshness fields, and know what the fail-open caveat means for your traffic.

# DNC Scrubbing: Sources, Freshness, and the Check Endpoint

A Do-Not-Call scrub answers one question before you send: **is this
number on a list that blocks or restricts contacting it?** Orbit checks
your own lists (contact flags, DNC entries, suppression, consent), the
platform-wide list, the US federal/state/TCR feeds, and several
international national registries — and the same chain the send path
enforces is readable as an API, so you can pre-flight a cold list
without risking a violating send.

This page is the end-to-end picture: when scrubbing matters, how to
turn it on, where the synced data comes from, how to read the check
endpoint's `source` and `last_synced_at`, and the one fail-open caveat
you need to manage. For the send-time gates that consume the result,
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 a given number is off-limits to you depends on your
  jurisdiction, your recipients, and what you send. Confirm with
  qualified counsel.
</Warning>

***

## When DNC scrubbing matters

DNC obligations come from more than one registry, and they layer:

* **US federal DNC (FTC National Registry).** Telemarketing calls and
  marketing texts to a federally registered number are restricted; a
  scrub against the federal registry is the baseline defense before any
  outbound campaign.
* **US state registries.** Several states run their own DNC lists on
  top of the federal one. A number can be absent federally but
  registered in, say, Pennsylvania — so a federal-only scrub is not
  complete for US traffic.
* **TCR Universal Opt-Out.** The Campaign Registry's universal opt-out
  list captures numbers that opted out of A2P texting at the registry
  level. Honoring it is part of US 10DLC texting hygiene regardless of
  your own suppression list.
* **International national registries.** Outside the US, equivalent
  national lists apply — the UK TPS and CTPS, Australia's Do Not Call
  Register, India's NDNC. These are per-jurisdiction: you scrub a UK
  campaign against UK registers, which is why the check endpoint
  accepts a `country` filter (below).

None of this replaces your own suppression layer — a recipient who
replied STOP to *you* is restricted regardless of any national
registry. The scrub chain treats your suppression, consent opt-outs,
and the national registries as one combined answer.

***

## Enable the tenant toggle

The check endpoint is **off by default** per organization. Until you
opt in — or until Orbit has a feed synced for the platform — the
endpoint returns `403 DNC_SYNC_NOT_ENABLED`:

```json theme={null}
{
  "error": {
    "code": "DNC_SYNC_NOT_ENABLED",
    "message": "DNC check endpoint requires the per-org opt-in flag ...",
    "status": 403
  }
}
```

Turn it on in your organization's compliance settings (the
`dnc_sync_enabled` setting — see the
[posture map](/compliance/posture-overview), which lists this toggle
and its default). One flip enables both the single-number check and the
bulk scrub.

<Note>
  **The gate auto-retires once feeds are synced.** The org toggle is
  the acknowledgment that, while no federal feed is synced, Orbit is
  not scrubbing against the FTC register for you. Once the platform has
  a federal/state/TCR (or international) snapshot synced, the endpoint
  serves directly and the 403 gate no longer applies — the
  `federal_feeds_synced` field on every response tells you which state
  you're in. Your own DNC, suppression, and consent enforcement on the
  send path is unaffected either way.
</Note>

***

## Where the list comes from (feed wiring)

The synced DNC data arrives from upstream feeds, refreshed daily into
the platform-wide DNC list. What an operator configures determines
which of the `source` values you see back from the check endpoint.

**These are operator-level environment variables.** On Orbit's hosted
(SaaS) platform they are set for you; the SaaS operator controls which
feeds are live. They matter to you directly only if you self-host Orbit
— in that case configure them in your deployment:

* **`DEVOTEL_DNC_FEED_FILE`** — a mounted volume containing a procured
  federal/state/TCR snapshot. The daily sync reads it, parses each
  opted-out number, and upserts it into the platform DNC list tagged
  with its source (`federal_dnc`, `state_dnc:XX`, or `tcr`).
* **`DEVOTEL_DNC_FEED_URL`** — an authenticated download of the same
  snapshot, as an alternative to a mounted file. File takes precedence
  when both are set.

The sync runs **once per UTC day** (feeds publish once per business
day; the run targets 03:00 UTC so a morning send window sees fresh
data). Re-running the same snapshot is a no-op beyond refreshing the
timestamp — the upsert is idempotent.

**Fallback: direct TCR pull.** When no snapshot file/URL is configured
but the paid TCR partner credentials are set — `DEVOTEL_TCR_API_KEY`
together with `DEVOTEL_TCR_PARTNER_ID` — the daily sync pulls the TCR
Universal Opt-Out registry directly from the partner API and upserts it
as `source: "tcr"`. This gives the TCR scrub from the existing TCR
subscription without an out-of-band CSV.

<Note>
  The same snapshot the daily sync writes to the platform DNC list is
  also loaded into memory by the API at boot, so the check endpoint can
  answer a federal/state/TCR lookup without waiting for the next daily
  write. `federal_feeds_synced: true` on the response means that
  in-memory feed is live.
</Note>

***

## Read the check endpoint

`GET /compliance/dnc/check` reports whether one E.164 number is on any
DNC source the send path enforces. It is read-only — it performs no
send.

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

A hit returns:

```json theme={null}
{
  "on_dnc": true,
  "source": "federal_dnc",
  "last_synced_at": "2026-08-26T03:00:12.441Z",
  "jurisdictions": ["US Federal DNC"],
  "federal_feeds_synced": true,
  "intl_feeds_synced": false
}
```

A clear number returns `"on_dnc": false`, `"source": "none"`, and an
empty `jurisdictions`.

The fields that carry the posture:

* **`source`** — which source produced the first hit, one of
  `federal_dnc`, `state_dnc` (a US state registry), `tcr` (the TCR
  Universal Opt-Out), `intl_dnc` (an international national registry),
  `suppression` (your own suppression layer), or `none` (not on any
  list). `jurisdictions` lists **every** matching origin, so a number
  on both the federal registry and a state registry surfaces both.
* **`last_synced_at`** — the freshness timestamp of the matching
  source: when the federal/state/TCR snapshot (or the row it hit) was
  last synced. The daily sync stamps a heartbeat row every run, so
  this field always carries a "last synced" signal for the feed-backed
  sources rather than going silent when no row matches. Your own
  suppression/consent sources have no sync, so for a suppression-only
  hit the field reflects the opt-out event itself.
* **`federal_feeds_synced` / `intl_feeds_synced`** — whether the API
  has actually ingested a US federal/state/TCR snapshot and an
  international national-registry snapshot, respectively. Read these on
  every response: they distinguish "the number is clear" from "the
  number is clear **and** a federal scrub backed that answer."

**Per-jurisdiction filter.** Add `country` (ISO alpha-2) to scope the
international registry lookup to one jurisdiction:

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

With `country=GB`, only UK registers (TPS/CTPS) can produce an
international match — a UK-bound campaign is never flagged on an
unrelated country's record.

**Bulk.** To scrub a whole list or campaign audience in one call, use
`POST /compliance/dnc/scrub` with a `phones` array (up to 500 numbers
per request; duplicates are removed). It runs the same chain per number
and returns per-number verdicts plus `on_dnc` / `clear` counts. The
same org gate applies.

***

## How the send side consumes it

The check endpoint is a **read** of the same chain the send path
**enforces**. You don't wire the scrub into your sends — Orbit already
does:

* **Messaging (SMS / MMS / WhatsApp / …).** Before dispatch, the send
  path answers "is this recipient opted out?" against the same DNC
  sources — your contact DNC flag, your DNC list, the suppression list
  (the cross-channel STOP signal), the platform-wide DNC list (where
  the daily federal/state/TCR feed lands), and recorded consent
  opt-outs. A hit drops the send.
* **Voice / dialer.** The dialer runs the same compliance chain before
  originating a call — cross-channel STOP, contact DNC, platform DNC,
  and consent — so a number that opted out by SMS is not then
  voice-dialed. The send path fails **closed**: if the compliance check
  itself can't complete, the call is deferred rather than dialed.

So the endpoint's job is pre-flight, not enforcement: query it (or the
bulk scrub) on a cold list **before** a campaign to remove numbers the
gate would block anyway — and to surface them for your own records.

***

## The fail-open caveat

The read endpoint is **fail-open at the source layer**: if one DNC
source can't be queried, the check continues to the others and returns
the best answer it can. The concrete consequence you must understand:

> **While no federal feed is synced, a number that is only on the FTC
> federal register reads back clear** (`on_dnc: false`).

That is why the endpoint is gated until you opt in, and why
`federal_feeds_synced` travels on every response. Two operational rules
follow:

1. **Don't treat a clear result as a federal safe-harbor unless
   `federal_feeds_synced: true`.** A clear verdict with
   `federal_feeds_synced: false` only means your own lists, suppression,
   consent, and the platform list didn't flag the number.
2. **The strict gate is the send path.** The read path is fail-open by
   design; the send path is not. A false negative from a pre-flight
   query never becomes a send — the send-time chain re-checks the same
   sources and blocks what it finds.

On Orbit's SaaS, treat `federal_feeds_synced: false` as "no federal
scrub is backing this answer yet" and weigh that in whether you rely on
the pre-flight for a US campaign. Self-hosters close the gap by
configuring a feed (above).

***

## Verify before a campaign

Pre-flight a campaign audience with the bulk scrub, then act on the
verdicts:

```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", "+14155550101"],
    "country": "GB"
  }'
```

```json theme={null}
{
  "results": [
    { "phone": "+14155550101", "on_dnc": true, "source": "federal_dnc", "jurisdictions": ["US Federal DNC"] },
    { "phone": "+442071838750", "on_dnc": false, "source": "none", "jurisdictions": [] }
  ],
  "summary": { "total": 2, "on_dnc": 1, "clear": 1, "duplicates_removed": 1 },
  "federal_feeds_synced": true,
  "intl_feeds_synced": false
}
```

Remove the `on_dnc: true` numbers from the audience before sending.
Check `federal_feeds_synced` / `intl_feeds_synced` on the same response
to know which registries actually backed the verdicts, and check
`last_synced_at` per result to confirm the data is fresh. For a larger
list, page it in chunks of 500 numbers.

***

See also:

* [Send Gates](/compliance/send-gates) — the quiet-hours / DNC / RND /
  RMD send-time gates and emergency stop.
* [Batch DNC Pre-Flight Scrubbing](/guides/dnc-preflight-scrub) — the
  campaign workflow around the bulk scrub: gating, chunking, and routing
  flagged numbers.
* [Opt-Out & Suppression Lists](/compliance/opt-out-suppression) — how
  a recipient's STOP lands on the suppression layer the DNC chain reads.
* [Your Tenant Compliance Posture](/compliance/posture-overview) — the
  toggle map, including this page's `dnc_sync_enabled` row.
