Skip to main content

Opt-Out & Suppression Lists

A suppression list is the set of addresses you must never message again — people who replied STOP, unsubscribed, bounced, or complained. Honouring it is a legal requirement on every regulated channel, and Orbit treats it as a hard send-gate: a suppressed address is dropped before dispatch regardless of campaign, contact import, or API call. This page covers how suppression works, how to bulk-import an existing suppression list — for example when migrating from another platform — through a single CSV upload, and how to export the ledger back out for an audit. All endpoints below are rooted at https://api.orbit.devotel.io/api/v1/compliance.

How suppression happens

An address lands on the suppression list in several ways:
  • A contact replies with a STOP keyword on SMS/WhatsApp.
  • A contact opts out via the Preference Center.
  • You record an opt-out through the Consent API (opt_in: false).
  • You bulk-import a list (this page).
Each entry has a channel scope. The full scope set is: all, sms, voice, whatsapp, email, push, telegram, messenger, rcs. How the scope is chosen depends on the entry point:
  • Bulk CSV import infers the scope from the address type of each row: phone and WhatsApp addresses default to scope all — a STOP signal on a phone number suppresses every channel reachable on that number — while email addresses are scoped to email. A channel column overrides this per row (see Bulk CSV import).
  • The Consent API and the Preference Center always suppress scope all, regardless of whether the recorded identifier is a phone number or an email address. An opt-out through either of these entry points removes the contact from every channel.
However a phone number is suppressed, the voice and dialer gates honour it: a number that opts out on any channel stops receiving calls as well as messages. The mechanism differs by entry point. A bulk CSV import additionally mirrors phone rows onto the DNC list and flags the matching contacts. A STOP keyword, Preference Center, or Consent API opt-out is instead recorded with the all scope, which the voice and dialer gates read from the suppression list directly — the call is still blocked, but no separate DNC-list row or contact flag is written.

Bulk CSV import

POST /compliance/suppression-list/import accepts a multipart/form-data upload of a CSV file. It requires an admin or owner key and is rate-limited to 5 requests/minute.

Form fields

CSV format

The first row is a header. Column names are case-insensitive and position-independent, and common aliases are accepted: Each row must contain at least one of phone / email / wa_id. A single row may carry several address types — each produces its own suppression entry. Example:
If a channel column is present it overrides the default scope for that row and must be one of the scope values listed above.

Per-row results

The response reports outcomes per row. Accepted rows are written; others are classified, never silently dropped.
The two duplicate counters are reported separately and on purpose: intra_file_duplicates are repeats inside the file you just uploaded, while duplicates were already on your list from earlier. Neither is an error, and neither is silently swallowed — both are counted so your reconciliation adds up.

Validation reasons

Each errors[] entry carries a friendly reason and the source raw_line so you can fix and re-upload:

Limits

For volumes beyond 100,000 rows, split the file and import in batches — duplicate detection means re-importing overlapping ranges is safe.

When an import fails

Diagnose failures on two levels: HTTP-level rejections (nothing is written) and row-level classifications (the file is accepted but specific rows are not). HTTP-level rejections: Row-level classifications (the errors[] entries that accompany a successful import) map to causes as follows: Only the first 100 errors[] entries are returned with full detail — the invalid counter always reflects the true total. The dashboard wizard (below) packages the surfaced rows as a downloadable skipped.csv so you can fix and re-import just the failures.

Import from the dashboard

The same endpoint is wrapped by a guided wizard at Settings → Compliance → Opt-out lists → Import suppression list — the same CSV contract, no terminal required.
  1. Choose CSV file — pick a .csv under 25 MB. Use Download sample CSV in the dialog for a pre-formatted starting file.
  2. Set defaults (optional) — a Default country (ISO alpha-2) to normalise national-format numbers, and a free-text Reason stamped onto every accepted row.
  3. Preview — runs the import as a server-side dry run: nothing is written, and the dialog shows the accepted / already-listed / repeats-in-file / invalid breakdown before you commit.
  4. Confirm import — performs the committing write. If any rows were invalid, download skipped.csv to fix and re-import them.
The wizard also enforces the file-type and 25 MB checks client-side, so a mistyped export fails before it ever reaches the API.

Export the suppression list

GET /compliance/suppression-list/export downloads the suppression ledger — the symmetric counterpart to the import above. Use it to prove to a regulator or auditor which addresses were suppressed at a given point, including bulk-imported numbers with no matching contact.
Query parameters: Each row carries suppression_id, channel, address, the derived status (active or revoked), reason, source, contact_id (empty for bulk-imported addresses with no contact), notes, and the suppressed_at / revoked_at / created_at timestamps. Access is restricted to owner and admin keys, and every export run is written to the audit log. When the ledger exceeds 50,000 rows the CSV response carries an X-Export-Truncated: true header (the JSON equivalent sets truncated: true) — narrow by channel or export consecutive date windows to capture the tail.

Verify a suppression took effect

Trust but verify: after an import (or any opt-out event), confirm the send gate actually fences the address before you hand the list to a campaign.
  1. Send a test message to the suppressed address. A direct API send to a suppressed recipient fails synchronously with HTTP 422 and the error code RECIPIENT_OPTED_OUT. In sandbox mode no carrier is touched and no balance is deducted; any recipient ending in 8 also resolves to the simulated blocked delivery receipt, which is the carrier-side view of the same fence.
    A campaign send to the same address behaves differently by design: the recipient is skipped silently (status: "skipped", reason: "opted_out") so the batch keeps moving — check the campaign’s per-recipient report rather than expecting an error.
  2. Confirm the entry is on the ledger. Export with the query above (status=active is the default) and check the address appears with the expected channel scope. The export is the source of truth every send gate reads — if the row is active there, the fence is up.
The two checks answer different questions: step 1 proves enforcement (the gate fires), step 2 proves scope (the entry exists with the channel you intended).

Removing a suppression (re-opt-in)

To bring an address back, record a fresh opt-in through the Consent API (opt_in: true). That revokes the matching suppression entry and clears the STOP-fence. Never re-message a previously suppressed contact without a documented, fresh consent event. Confirming the fence is down. Export with status=revoked and find the address: the row is still there for audit with status: revoked and a revoked_at timestamp — suppression history is never deleted, only revoked. Then send a small test message to the address as in Verify a suppression took effect: a successful submission (no RECIPIENT_OPTED_OUT) confirms the gate no longer fires on revoked history. Until both checks pass, treat the address as still fenced.
  • Consent Management — record and look up per-channel consent, and the revocation-parity treatment this page’s scope table is a part of: why a keyword STOP asserts scope all, and how /compliance/consent/history evidences the revocation endpoint the fence was written from.
  • Send Gates — quiet hours, DNC, RND, RMD, emergency stop, and the preference center.
  • DSAR — how delete / opt_out requests reach suppression.
  • API Reference → Opt-outs — opt-out and suppression endpoint schemas.