Skip to main content

Message suppression model

Message suppression is the content-hash duplicate guard: a send is silently skipped when the identical body already reached the same contact on the same channel inside a window you choose. It is a safety net against double-sends — not a compliance control, and not the contact block list. Three mechanisms in Orbit all answer to “suppression” and are routinely confused for one another. This page tells them apart: which question each answers, where each sits in the send pipeline, which console and API each lives behind, and which one to reach for.

One-line semantic

Suppression list is compliance-owned; message suppression is a safety net; consent suppression is an opt-out gate.
Read the table top-down and the words stop colliding: one is a policy, one is recipient state, one is a ledger. None of the three replaces another.

The three concepts side by side

Duplicate-content suppression — metadata-based, relative suppression of identical bodies. The policy is a single opt-in object per organization. On every send the pipeline hashes the whitespace-normalized body text and claims the (recipient, channel, body-hash) marker as one compare-and-set; an identical body in flight within the window loses and is skipped. Manipulation primitives: GET / PUT / DELETE /api/v1/message-suppression, plus a dashboard wrapper. Consent suppression — per-channel gate driven by ledger state. Consent is recorded per contact with a lawful basis, and mirrored into per-channel flags the send path checks. An opt-out writes a fresh consent record, flips the flags, and (for a phone number reaching voice) writes a suppression row — see Consent, opt-out, and suppression. The gate is a refusal, not a skip: direct sends get 422 RECIPIENT_OPTED_OUT; campaign sends to opted-out recipients are skipped at the recipient-list level. Suppression list — per-row, per-channel, CSV-imported compliance block list. The same ledger of (address, channel-scope) rows powers both the migrations you bulk-import (POST /compliance/suppression-list/import) and the rows an opt-out writes through its own entry point. Scope defaults (all for phone-type addresses, email for email addresses) and per-row overrides are documented in Opt-Out & Suppression Lists. Two of these land in the same underlying ledger — that is why the console renders the consent inspector and the import wizard next to each other — while the duplicate-content guard is a separate Redis-backed marker store. “Suppressed” in an audit most often means “blocked by a suppression-list row”; which of the three did the work is recorded on the message outcome and in the recipient’s consent history.

Where each sits in the pipeline

Each outbound send walks the admission chain in a fixed order (the complete chain — wallet posture, quiet hours, throughput — is in Outbound send gating):
  1. Opt-out / compliance gate — consent suppression and the suppression list both land here. A recipient whose flags or ledger rows block the channel is refused first; nothing downstream ever runs for them.
  2. Duplicate-content suppression — the message-suppression policy this page organizes. Runs only for recipients who cleared the gate, and skips an identical re-body without burning quota.
  3. Frequency cap — the per-channel count axis (“how many sends in N hours”), consulted only after suppression has had its say; a suppressed duplicate never consumes a cap slot.
  4. Dispatch — the message is handed to the channel provider.
Because the gate comes first, pre-gate ordering of the opt-out checks means supplements never apply to a blocked recipient: you cannot suppress-or-cap your way around an opt-out, and a skipped duplicate can only ever occur for a contact who is fully eligible to send to.

Why the names collide in the dashboard

The console hosts all three under Settings, and two of them both say “suppression”:
  • Settings → Message suppression manages the duplicate-content policy — the content-hash guard on this page — as a thin wrapper over the same GET / PUT / DELETE /api/v1/message-suppression route the API exposes.
  • Settings → Opt-out lists & compliance is the compliance surface: suppression-chain entries and the CSV import/export wizard operate on the suppression list ledger, the second mechanism.
A third surface, the consent inspector under Audience, shows the consent-record ledger the opt-out gate reads. If a search for “suppression” lands you in the wrong console, the disambiguation above — policy vs recipient state vs ledger — is the faster route to the right one than reading three guides.

Propose or refuse — choosing the right mechanism

  • Propose the suppression list for compliance-owned runbooks: migrating a legacy “never contact” export, onboarding a litigator-scrub file, honouring a partner’s unsubscribe feed. Its import is rate-limited, deduplicated, and dry-run-able; export it back out when legal asks for the ledger.
  • Refuse the suppression list for consent-primacy questions. “Why was this contact blocked?” and “under which basis did they opt back in?” are questions about the consent ledger — answer them from consent records and the recipient state, not from CSV rows. Re-messaging a suppressed contact without a documented fresh consent event is what the re-consent semantics exist to prevent.
  • Propose message suppression when campaigns, flows, or retry logic can double-fire the same body and you would rather drop the duplicate silently than have a customer receive it twice. Keep it scoped to marketing categories so OTPs and receipts are never held back.
  • Refuse message suppression as a substitute for honouring an opt-out. It deduplicates intended sends; it does not block a channel, and a contact who said STOP must be refused at the gate — not kept eligible and occasionally deduped.

See also