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

# Carrier Deactivation (Churn) Scrub: List Hygiene Against Carrier Disconnects

> Turn on the carrier deactivation scrub, pre-flight single numbers or whole lists against the carrier disconnect feed, understand how the automatic pre-send guards enforce it, and know what the fail-open caveat means for your traffic.

# Carrier Deactivation (Churn) Scrub: List Hygiene Against Carrier Disconnects

The carrier deactivation scrub answers one question before you re-contact a
recipient: **did the subscriber's carrier deactivate this number since you
last had a valid relationship with them?** Numbering carriers publish a daily
feed of subscriber numbers that were deactivated or disconnected — the
subscriber cancelled the line, ported away and let it lapse, or the carrier
reclaimed the number. Scrubbing against that feed keeps churned numbers out
of your lists before they cost you throughput or deliver to a stranger.

This page is the end-to-end picture: what the scrub is, how to turn it on,
how to pre-flight one number or a whole list, how the automatic pre-send
guards enforce it on SMS/MMS, voice, and Verify/OTP, and what the fail-open
caveat means. For the other scrub layers this complements — the FCC
Reassigned Numbers Database and Do-Not-Call registries — see the comparison
table below and [Send Gates](/compliance/send-gates).

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

<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 the deactivation scrub matters

Continuing to text or call a deactivated number has two costs a sender pays
either way:

* **Wrong-recipient delivery.** Once a carrier deactivates a number, it can
  be reassigned to a new subscriber. Messaging it after the disconnect risks
  delivering marketing texts or even one-time codes to a stranger — a 47
  U.S.C. § 227 (TCPA) exposure for US traffic, and reputational exposure
  everywhere.
* **Dead-number burn.** Every send to a disconnected number still consumes
  A2P throughput and counts against your sender reputation with the carrier.

The sender's obligation is to scrub **before every re-contact** — a clean
check from last month says nothing about a number deactivated yesterday.
That is why Orbit wires this feed into the automatic pre-send guards
(below) instead of leaving it as a manual pre-flight step.

**Deactivation is distinct from the two other scrub surfaces Orbit ships:**

* The **DNC scrub** answers "did the *person* ask to stop?" — federal,
  state, TCR, and international registries plus your own suppression layer.
* The **RND check** answers the TCPA safe-harbor question "was this number
  *permanently disconnected* after my consent date?" — a per-number legal
  gate against reassignment.

The deactivation scrub answers the operational-hygiene question above —
"did the *carrier* deactivate this number since I last saw it valid?" — so
a list stays clean regardless of consent or opt-out state. See the
[comparison table](#deactivation-vs-rnd-vs-dnc) for the full matrix.

***

## Enable the tenant toggle

The scrub endpoints and the automatic send-path guards are **off by
default** per organization. Until you opt in, the check and scrub endpoints
return `403 DEACTIVATION_SCRUB_NOT_ENABLED` and the send-path guards skip.
The opt-in is the `deactivation_scrub_enabled` setting on your
organization's compliance settings; any authenticated member can read it,
while only an org **owner** or **admin** can change it.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/compliance/deactivations/settings" \
  -H "Authorization: Bearer $ORBIT_API_KEY"
```

```json theme={null}
{
  "enabled": true,
  "feed_synced": true
}
```

Turn it on (owner/admin only):

```bash theme={null}
curl -X PUT "https://api.orbit.devotel.io/api/v1/compliance/deactivations/settings" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

The `feed_synced` field is read-only: it reports whether a carrier
deactivation snapshot is actually loaded. Until one is synced, every
verdict stays `no_data` — see the [fail-open caveat](#the-fail-open-caveat).
One flip enables both the API surface and the automatic pre-send guards;
the write is auditable in the compliance audit log.

***

## Check a single number

`GET /deactivations/check` reports whether one E.164 number was deactivated
by the subscriber's carrier. Supply `last_seen` — the date you last
validated the number had a live relationship with the subscriber — to
ignore deactivations that predate the current relationship.

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

```json theme={null}
{
  "status": "deactivated",
  "should_scrub": true,
  "last_deactivation_date": "2026-08-14",
  "last_seen_date": "2026-07-01",
  "feed_synced": true,
  "reason": "The subscriber's carrier deactivated this number; scrub it from the list to avoid messaging a reassigned or dead number."
}
```

The fields that carry the verdict:

* **`status`** — one of `deactivated` (carrier churn on record — scrub it),
  `active` (no relevant deactivation on record — keep it), or `no_data` (no
  feed synced — no verdict can be asserted; keep, at your own risk).
* **`should_scrub`** — true only when `status` is `deactivated`. A hygiene
  gate never scrubs a number the feed has not positively flagged.
* **`last_deactivation_date`** — the most-recent carrier deactivation on
  record for the number, or `null` when there is none.
* **`last_seen_date`** — the filter date you supplied, echoed back.
* **`feed_synced`** — false until a carrier snapshot is loaded. While false,
  every verdict is `no_data`.
* **`reason`** — a human-readable explanation, suitable for audit review.

Omitting `last_seen` means **any** deactivation record on file scrubs the
number — the conservative default. With a `last_seen` date, deactivations
strictly before it are ignored (the current subscriber relationship postdates
them).

***

## Scrub a batch before a campaign

`POST /deactivations/scrub` runs the same verdict against up to **1,000**
numbers per request — the list-hygiene step before a campaign. A top-level
`last_seen` applies to every number unless a per-number `last_seen`
overrides it.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/compliance/deactivations/scrub" \
  -H "Authorization: Bearer $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "last_seen": "2026-07-01",
    "numbers": [
      { "phone": "+14155550101" },
      { "phone": "+12125550102", "last_seen": "2026-01-15" }
    ]
  }'
```

```json theme={null}
{
  "feed_synced": true,
  "checked": 2,
  "scrub_count": 1,
  "kept_count": 1,
  "results": [
    {
      "phone": "+14155550101",
      "status": "deactivated",
      "should_scrub": true,
      "last_deactivation_date": "2026-08-14",
      "last_seen_date": "2026-07-01",
      "reason": "The subscriber's carrier deactivated this number; scrub it from the list..."
    },
    {
      "phone": "+12125550102",
      "status": "active",
      "should_scrub": false,
      "last_deactivation_date": "2025-11-30",
      "last_seen_date": "2026-01-15",
      "reason": "The only carrier deactivation on record predates the date you last validated this number..."
    }
  ]
}
```

Drop the `should_scrub: true` rows from the audience before launching.
For larger lists, page the batch in chunks of 1,000 numbers. Both endpoints
are read-only — no send is performed.

***

## How the send path enforces it

Opting in does more than unlock the API surface: the same feed powers
automatic pre-send guards, so enforcement does not depend on someone
remembering to pre-flight.

* **SMS / MMS sends.** Before dispatch, the message pipeline looks up your
  most-recent prior-express consent date for the recipient (unlike the
  manual endpoint, which defaults to any-record). A deactivation on or after
  that date refuses the send with
  `MESSAGING_NUMBER_DEACTIVATED` (HTTP 422). A deactivation that predates
  your consent does not block a legitimately consented send. Campaign
  bulk-sends route through the same send pipeline, so the guard applies
  there too.
* **Voice / dialer.** The dialer evaluates the same carrier deactivation
  verdict before originating a call to a NANP (+1) destination — the carrier
  deactivation feed today covers NANP numbers, so non-NANP destinations
  are skipped rather than mis-gated.
* **Verify / OTP.** A one-time code sent on a phone channel (SMS, MMS,
  voice, or flashcall) is refused with `VERIFY_NUMBER_DEACTIVATED`
  (HTTP 422) whenever any deactivation record is on file. There is no
  consent ledger for a login code, so the guard uses a strict
  disconnect-presence model — any carrier record means the code could reach
  a reassigned stranger, and verification must ask the user to confirm the
  number another way.

Every refusal writes an audit entry, so a compliance review can prove the
guard fired. All guards are **fail-open**: a lookup error, a missing feed,
or an unset opt-in declines to block, so this additive hygiene layer never
black-holes a send. The strict enforcement runs on the send path itself —
a false negative from a manual pre-flight query never becomes a send.

***

## The fail-open caveat

That fail-open posture means you must read `feed_synced` on every response
before acting on a clear verdict:

> **Until a carrier deactivation snapshot is synced, every query returns
> `no_data` and clear** (`should_scrub: false`) — for every number.

Two operational rules follow:

1. **Treat a clear verdict as "no disconnect seen so far," never as
   safe-harbor.** A `no_data` result means the platform cannot assert the
   number is churned *or* clean — the algorithm deliberately degrades to
   default-keep rather than claim a clean verdict it could not back.
2. **A scrub that skipped still counts as scrubbed-only-if-checkable.** On
   the SaaS platform the feed is synced centrally; on self-hosted
   deployments the operator configures the carrier snapshot. Either way,
   weigh `feed_synced` before relying on a US campaign pre-flight.

***

## Recover after a block

A deactivated verdict is recoverable, but only through the recipient — not
by retrying:

1. **Drop the destination from active lists** until you re-verify the number
   with the subscriber.
2. A send-path block clears when a **new consent grant** (prior express) is
   recorded that is dated *after* the carrier's `last_deactivation_date` —
   the consent-date filter then treats the current relationship as newer
   than the disconnect.
3. For Verify/OTP, the block persists as long as any deactivation record is
   on file; confirm the number belongs to the user through another channel
   before re-attempting.

***

## Deactivation vs. RND vs. DNC

| Question                                                                               | Endpoint                                         | What it checks                                                                                              | Block codes                                                 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| Did the subscriber's carrier deactivate this number since my last valid relationship?  | `/compliance/deactivations/check` + `scrub`      | Carrier disconnect/churn feed vs. an optional last-validation date (or the consent ledger on the send path) | `MESSAGING_NUMBER_DEACTIVATED`, `VERIFY_NUMBER_DEACTIVATED` |
| Was this number permanently disconnected **after my consent date** (TCPA safe-harbor)? | `/compliance/rnd/check`                          | FCC Reassigned Numbers Database: permanent-disconnect date vs. consent date                                 | `VERIFY_RND_REASSIGNED` and the messaging RND equivalent    |
| Did the person ask to stop / is the number on a restricted registry?                   | [`/compliance/dnc/check`](/compliance/dnc-scrub) | Federal/state/TCR/international DNC registries plus your suppression and consent layers                     | DNC / suppression refusal codes                             |

Run all three on a campaign audience and prune on any hit — they answer
orthogonal questions. See [Send Gates](/compliance/send-gates) for how the
send-time chain layers them, and
[Troubleshooting Verify OTP](/troubleshooting/verify-otp) for recovering
from a `VERIFY_NUMBER_DEACTIVATED` refusal.

***

See also:

* [DNC Scrubbing](/compliance/dnc-scrub) — the registries-plus-suppression
  layer that complements the deactivation scrub.
* [Send Gates](/compliance/send-gates) — the quiet-hours / DNC / RND /
  deactivation send-time gates and emergency stop.
* [Troubleshooting Verify OTP](/troubleshooting/verify-otp) — diagnosing a
  refused verification, including carrier-deactivation blocks.
* [Error Codes](/reference/error-codes) — the full `MESSAGING_NUMBER_DEACTIVATED`
  and `VERIFY_NUMBER_DEACTIVATED` reference rows.
* [Your Tenant Compliance Posture](/compliance/posture-overview) — the
  toggle map, including the `deactivation_scrub_enabled` row.
