Skip to main content

Context

Two mechanisms that look similar and are not, and conflating them is a real bug. Consent is what the person chose, and it lives in core. Suppression is what the system or the provider imposed: a hard bounce, a spam complaint, an unsubscribe click. A person can consent and still be suppressed — the address simply does not work — and a suppressed address must stay suppressed even for transactional messages, because a bounce does not become deliverable because the message is important. The preference center is the third layer: within what they consented to, a member chooses the granularity. Making it one click from any marketing message is not a courtesy; a hard-to-find unsubscribe is what generates spam complaints, and complaints damage the shared sending reputation of every tenant.

Scope (normative)

  • messaging.suppressions: global per tenant, per channel identifier.
  • Automatic suppression on hard bounce, complaint and unsubscribe.
  • messaging.preference_settings: per member, per channel, per category.
  • One-click unsubscribe from any marketing message, including the list-unsubscribe header.
  • Quiet hours and frequency caps configuration at tenant and campaign level.
  • Suppression survival through erasure as a hashed identifier.

Non-scope (normative)

  • Consent, which belongs to core and is read here.
  • Manual suppression import from a previous provider — a later import feature, though the table supports it.
  • Re-engagement of suppressed addresses. A suppression is only lifted by the member acting.

Behaviour (normative)

  1. A suppression is keyed by (tenant, channel, identifier) — the email address or push token, not the contact. If the same address belongs to two contacts, both are suppressed, because the address is what bounced.
  2. Every send checks suppressions, no exceptions, including transactional (FS-MSG-0001 rule 3).
  3. Hard bounces and complaints suppress automatically, in the same transaction as the status (FS-MSG-0003). Soft bounces do not: a full mailbox is temporary.
  4. Unsubscribe is one click, no login, from a signed link that identifies the member without authenticating them. Requiring a login to unsubscribe generates complaints.
  5. The List-Unsubscribe and List-Unsubscribe-Post headers are set on every marketing message, so a mail client’s own button works.
  6. A suppression can be lifted only by the member re-subscribing, never by the tenant. A tenant un-suppressing an address that complained is exactly the behaviour that gets a domain blocked.
  7. The preference center is per channel and per category. Transactional appears as informational and cannot be switched off (DEC-E3).
  8. Quiet hours default at tenant level with campaign override; frequency caps apply to marketing only (DEC-E5).
  9. Erasure deletes the profile but the suppression survives as a hashed identifier with no profile attached (DEC-J3).
  10. FORBIDDEN: a tenant-facing endpoint that deletes a suppression · a marketing message without a working unsubscribe · counting transactional against a frequency cap.

Data (normative)

The identifier is stored hashed so a suppression list is not a usable contact list.

API (normative)

The unsubscribe route lives at the root, outside /v1, because it is a link in an email that must stay short, stable and human-readable. Documented exception to DEC-D1, like the OAuth routes.

Events (normative)

None beyond the status events FS-MSG-0003 emits. A suppression is a consequence, not an occurrence.

Acceptance criteria (normative)

  1. A suppressed address receives nothing, including transactional and including a different contact sharing that address.
  2. A hard bounce suppresses in the same transaction; a soft bounce does not.
  3. Unsubscribe works from the signed link without a login, in one request.
  4. List-Unsubscribe headers are present on every marketing message and function in a real client.
  5. No tenant-facing endpoint removes a suppression.
  6. The preference center cannot switch off transactional.
  7. Frequency caps count marketing only.
  8. After erasure the suppression still blocks that address, with no profile attached.
  9. Negative: the suppression list cannot be exported as usable contact data — identifiers are hashed.

Execution

Single slice, synchronous command. The unsubscribe route is public and signed; preference reads are served from the same cache as the cascade configuration.

Open questions

Changelog

Delivery record

Not implemented yet.