Skip to main content

Consent, opt-out, and suppression

Three stores — consent_records, channel_preferences, and the suppression_list — hold a contact’s recipient state. Every opt-in or opt-out, from any entry point, writes all three together, so a send gate never has to ask which door consent came through: the answer for a (contact, channel) pair is the same from everywhere. This page is the model the individual guides assume: what the stores are, which entry points mutate them, how channel scoping works, and where the state plugs into the send chain.

The three stores

A write also flips a short-lived STOP-fence cache key so in-flight campaign batches converge on the new state within minutes rather than on the next contact read. The STOP-fence is a convenience, never the source of truth — the authoritative state lives in the stores above. Because every entry point maintains all three surfaces atomically, the stores stay consistent by construction: the audit trail, the read mirror, and the gate verdict are one state, not three that can drift apart.

Entry points that write recipient state

Recipient state changes from every direction consent can enter:
  • Inbound STOP / START / HELP replies — the opt-out keyword lists layer that recognizes the reply writes the opt-out.
  • The Preference Center — contact self-service per-channel toggles.
  • The Consent APIPOST /compliance/consent, the programmatic record (opt_in: true / false) covered by Consent Management.
  • The public consent form — a hosted, unauthenticated submission page you can enable per tenant; every submission writes the same three stores as the carrier-side keyword handlers. See Public consent form.
  • Bulk CSV import — migrating an existing suppression list writes suppression entries directly; see Opt-Out & Suppression Lists.
  • DSAR requests — a delete / opt-out request resolves into the same stores; see DSAR.
Whichever door the write arrived through, the downstream effect is identical: the send gates read the same stores.

Scopes and channel semantics — all vs per-channel

Each suppression entry carries 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:
  • Keyword replies, the Preference Center, the Consent API, and the public consent form always write scope all — an opt-out through any of these removes the contact from every channel.
  • Bulk CSV import infers the scope per row from the address type: phone and WhatsApp addresses default to 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 in the CSV overrides the default per row.
Scope all on a phone number reaches voice too: the voice and dialer gates read the suppression list directly, so a number that opts out on any channel stops receiving calls as well as messages.

Where recipient state plugs into the send chain

Recipient state is one gate in the outbound admission chain — the per-recipient block-list check. Its position in the chain, the error it returns (422 RECIPIENT_OPTED_OUT on direct sends), and how campaign sends skip instead of erroring are covered by Outbound send gating — the chain walks wallet posture, quiet hours, duplicate-content suppression, recipient block lists, frequency caps, and throughput in a fixed order, and this page does not repeat that ordering. What matters here: the block-list gate reads channel_preferences and the suppression_list, so the moment an opt-out lands, the next send to that recipient is a refusal or a skip, not a dispatch. Consent is reversible in both directions, and a revocation is never a deletion:
  • Opt-out writes a new consent record, flips the contact’s channel_preferences flags, and inserts an active suppression row.
  • Opt-in (re-consent) writes a fresh consent record, clears the flags, and revokes the matching suppression rows — sets revoked_at on the existing row rather than deleting it, so the ledger keeps the full history of “suppressed, then re-consented” for audit.
  • Expiring consent — an opt-in recorded with valid_until or expires_in_days ages out on its own: the lookup reports expired and requires_reconfirmation, and Consent Management documents the sweep endpoint that feeds a re-permission campaign.
Never re-message a previously suppressed contact without a documented, fresh consent event — the re-opt-in through the Consent API is also what clears the block, so the legal and the mechanical steps are the same call.

Interaction with the other gates

Consent, opt-out, and suppression answer one question — may I send to this recipient at all? The other gates answer different questions and run independently:
  • Quiet hours ask is it the right time, recipient-local, under my window? A fully consented recipient is still blocked outside the window.
  • Frequency caps ask has this contact already received too many sends in my window? — a count axis, blind to consent state.
  • Message suppression asks did this exact body already reach this recipient on this channel? — a content axis, blind to consent state.
Because the block-list check runs before the frequency-cap slot claim in the admission chain, an opted-out recipient never consumes a cap slot, and a duplicate-content skip never happens for a recipient who was blocked on consent grounds first. Configure each gate on its own axis — none of them substitutes for the others.

Tenant-owned controls

The recipient state on this page is your data and your responsibility: you capture consent, you honour revocations, and Orbit carries the records, evaluates them at send time, and keeps the audit trail. The one gate in the send chain a tenant cannot disable — the TCPA federal voice dialing window for US recipients — operates independently of recipient state and is covered in Outbound send gating. Recording consent in Orbit creates an auditable trail; it does not by itself make a send lawful. You remain responsible for obtaining valid consent and for the content you send.

See also