Skip to main content

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. All endpoints below are rooted at https://api.orbit.devotel.io/api/v1/compliance.
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.

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:
Turn it on in your organization’s compliance settings (the dnc_sync_enabled setting — see the posture map, which lists this toggle and its default). One flip enables both the single-number check and the bulk scrub.
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.

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

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.
A hit returns:
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:
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:
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: