---
id: CAP-PFM-NOT-020
title: Apply recipient protection and sender verification
feature: FEAT-PFM-NOT-07
product_area: platform
module: notifications
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-platform:docs/communications/architecture.md#15. Decisions for
    Andrew
  - >-
    services-platform:docs/communications/architecture.md#2026-10-09 — Final
    acceptance
purpose: >-
  Platform withholds unsafe recipients and selects a verified association sender
  when available.
outcomes:
  - >-
    Suppressions prevent applicable recipient sends, and sender checks select
    verified identities or the shared fallback.
  - >-
    Nonproduction delivery admits test addresses and the configured allowlist,
    which can include real mailboxes.
concepts:
  - CON-PFM-NOT-005
actors:
  - ACT-PFM-028
  - ACT-PFM-031
surfaces:
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: platform.communication-suppressions.read
    where_checked: api
    scope: tenant
  - key: platform.communication-suppressions.create
    where_checked: api
    scope: tenant
  - key: platform.communication-suppressions.update
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-PFM-NOT-020-01
    statement: >-
      Permanent bounces and complaints suppress the workspace address,
      withholding subsequent mail without provider submission.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#stores
      the next notice to that address suppressed, with the reason, and sends
      nothing
    origin: >-
      services-platform:docs/communications/architecture.md#2026-10-09 — Final
      acceptance
  - id: BR-CAP-PFM-NOT-020-02
    statement: >-
      Canonical addresses identify suppression entries; repeated suppression
      preserves the current reason and creates no duplicate entry.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/suppressions.integration.spec.js#records
      nothing for an address already suppressed, so a repeated bounce adds no
      second row
    origin: none
  - id: BR-CAP-PFM-NOT-020-03
    statement: >-
      Suppression-create permission allows an address and optional note;
      whitespace-only notes are omitted, and malformed or blank addresses
      produce field-specific refusals.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/suppressions.e2e.spec.js#records
      it as a manual suppression with the note staff gave
    origin: none
  - id: BR-CAP-PFM-NOT-020-04
    statement: >-
      Release requires an explanation and records actor and reason while
      retaining history; missing or already-released entries are not found; a
      released address may be suppressed again.
    kind: lifecycle
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/suppressions.e2e.spec.js#refuses
      a release that does not say why as 400
    origin: none
  - id: BR-CAP-PFM-NOT-020-05
    statement: >-
      Suppression lists are newest first and normally show active entries;
      Including released entries is optional; pages default to fifty and cap at
      two hundred.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/suppressions.queries.integration.spec.js#answers
      at most the 200 a page may hold, however many the caller asks for
    origin: none
  - id: BR-CAP-PFM-NOT-020-06
    statement: >-
      Reserved demonstration domains, including example.com relatives and .test,
      .example, .invalid and .localhost suffixes, are withheld in every
      environment; other request recipients continue independently.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/reserved-domain-suppression.integration.spec.js#withholds
      its message with the reserved-domain reason, so the history says why it
      was not sent
    origin: none
  - id: BR-CAP-PFM-NOT-020-07
    statement: >-
      Nonproduction sends allow only test mailboxes and configured permitted
      addresses or domains; other primary and copy recipients are withheld.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: documented
    source: 'services-platform:docs/email.md#The delivery policy'
    origin: none
  - id: BR-CAP-PFM-NOT-020-08
    statement: >-
      Verified workspace sender addresses take precedence over Rhythm's shared
      sender; Selection is cached per process for one minute; template and
      request settings determine display names.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/sender-identity/tenant-sender-address.integration.spec.js#sends
      from the shared Rhythm sender, so the tenant’s mail never waits for the
      customer
    origin: none
  - id: BR-CAP-PFM-NOT-020-09
    statement: >-
      Each workspace has one sender setting using domain, address or combined
      verification; Domain strategies require a domain; address-only omits it;
      Addresses match the verified domain exactly rather than a subdomain, and
      verified state includes its verification time.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/db/communication-sender-identities.table.integration.spec.js#refuses
      an address at a subdomain of it, which SES does not cover either
    origin: none
  - id: BR-CAP-PFM-NOT-020-10
    statement: >-
      Provisioning creates or adopts the identities needed for the selected
      strategy, retains applicable DNS instructions and existing verification
      state, and does nothing when no identity is requested.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/sender-identity/provision-sender-identity.integration.spec.js#leaves
      the setting pending, because nothing it did proves the customer published
      anything
    origin: none
  - id: BR-CAP-PFM-NOT-020-11
    statement: >-
      Provider refresh reports verified when any identity can send, pending
      while DNS is missing, and failed when none can send and a provider
      identity failed; the first verification time remains permanent.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/sender-identities.integration.spec.js#never
      clears that instant when a later reading is no longer verified
    origin: none
  - id: BR-CAP-PFM-NOT-020-12
    statement: >-
      Workspace deletion removes provider identities before the setting and
      reports deletion failure rather than claiming success while an identity
      remains; request and body cleanup is confined to the workspace prefix.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/sender-identity/provision-sender-identity.integration.spec.js#fails
      for an operator rather than reporting a sender gone that can still send
    origin: none
  - id: BR-CAP-PFM-NOT-020-13
    statement: >-
      Platform invitation and password messages always use Rhythm's Platform
      sender rather than association identity.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: 'services-platform:docs/email.md#Sending identities'
    origin: none
  - id: BR-CAP-PFM-NOT-020-14
    statement: >-
      Verification refresh reads provider state and writes the resulting setting
      and latest check time, preserving the first verified instant.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/communications/services/sender-identity/provision-sender-identity.ts:refreshSenderIdentityVerification
    origin: none
  - id: BR-CAP-PFM-NOT-020-15
    statement: >-
      Purge removes both newly created and adopted provider identities and
      verifies absence before recording removal.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/communications/services/sender-identity/provision-sender-identity.ts:purgeSenderIdentityIdentities
    origin: none
scenarios:
  - id: SC-CAP-PFM-NOT-020-01
    given: A member's address received a permanent bounce the previous week.
    when: CRM submits the next compliance message to that address.
    then: The message records suppression withholding without provider submission.
    provenance: inferred
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#stores
      the next notice to that address suppressed, with the reason, and sends
      nothing
    note: >-
      The test proves withholding on the next submission; the illustrative
      week-long interval follows from active suppression having no automatic
      expiry.
edge_cases: []
constraints: []
depends_on: []
related:
  - CAP-PFM-NOT-017
  - CAP-PFM-NOT-018
  - CAP-PFM-NOT-019
journeys:
  - JNY-PFM-NOT-006
open_questions: []
evidence:
  - >-
    Pinned evidence inventory 1:
    services-platform:docs/communications/architecture.md;
    services-platform:modules/communications/tests/e2e/suppressions.e2e.spec.js;
    services-platform:docs/email.md;
    services-platform:modules/communications/tests/integration/services/sender-identity/provision-sender-identity.integration.spec.js
  - >-
    Pinned evidence inventory 2:
    services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/suppressions.queries.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/sender-identity/tenant-sender-address.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/sender-identities.integration.spec.js
  - >-
    Pinned evidence inventory 3:
    services-platform:modules/communications/tests/integration/services/persistence/suppressions.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/dispatch/reserved-domain-suppression.integration.spec.js;
    services-platform:modules/communications/tests/integration/db/communication-sender-identities.table.integration.spec.js;
    services-platform:modules/communications/services/sender-identity/provision-sender-identity.ts
controls: [TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-006, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-PRV-004, TC-SEC-001, TC-SEC-002, TC-SEC-003, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Apply recipient protection and sender verification

## Purpose

Platform withholds unsafe recipients and selects a verified association sender when available.

## How It Works

Platform checks canonical recipient addresses against suppression and environment policy. Permanent bounce and complaint feedback adds suppression automatically. Staff can add suppression or release it with a recorded reason. Message and copy destinations receive protection checks independently. Sender provisioning creates or adopts the provider identities requested by the workspace. Refresh writes verification state and check times, and delivery uses verified identity or the shared sender. Deletion verifies provider removal before deleting the setting. Shared adopted identities remain an unresolved ownership and deletion risk.

The product should establish sender-identity ownership before allowing adopted identities to be deleted during workspace cleanup.

## Relationships

This outcome supplies communication records used by acceptance, sending and inspection.
