---
id: CAP-PFM-NOT-019
title: Inspect communication outcomes and provider feedback
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
  - >-
    services-platform:docs/communications/architecture.md#16.5 CC and BCC
    (Andrew, 2026-10-09)
  - >-
    services-platform:docs/communications/architecture.md#18. Rework 2026-10-10:
    queue pointers, S3 for request data and bodies, SQS owns retries
purpose: >-
  Staff and producing services can distinguish Platform submission activity from
  provider-reported delivery and engagement.
outcomes:
  - >-
    Authorized readers inspect communication requests, content and delivery
    outcomes.
  - >-
    Changed-message feeds filter by the supplied requesting service, whose
    caller ownership remains unverified.
concepts:
  - CON-PFM-NOT-005
actors:
  - ACT-PFM-028
  - ACT-PFM-030
  - ACT-PFM-031
surfaces:
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: platform.communication-messages.read
    where_checked: api
    scope: tenant
  - key: platform.communication-messages.read-content
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-PFM-NOT-019-01
    statement: >-
      Provider send, delivery, deferral, bounce, complaint, rejection, open and
      click feedback uses submission tags to find the workspace message;
      Untagged reports are acknowledged as unrelated; missing tagged messages
      retry and eventually dead-letter.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/communications/services/feedback/process-ses-event.ts:rhythm-message
    origin: none
  - id: BR-CAP-PFM-NOT-019-02
    statement: >-
      Event identity prevents duplicate recording; Delivery states only advance
      in precedence, first occurrences retain the earliest instant and counters
      increase for new reports, making report order immaterial.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#keeps
      the message delivered when a deferral arrives after the delivery
    origin: none
  - id: BR-CAP-PFM-NOT-019-03
    statement: >-
      Transactional mail also tracks opens and clicks; Proxy and scanner signals
      count as events but not likely-human activity; retained click links omit
      query strings.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#counts
      an open by an image proxy as an open, not as a likely human one
    origin: >-
      services-platform:docs/communications/architecture.md#15. Decisions for
      Andrew
  - id: BR-CAP-PFM-NOT-019-04
    statement: >-
      Dispatch state records Platform's actions separately from delivery state
      reported by the provider.
    kind: invariant
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#says
      what Rhythm did and what the provider reported, as separate facts
    origin: none
  - id: BR-CAP-PFM-NOT-019-05
    statement: >-
      Message-read permission exposes newest-first workspace messages with
      request, recipient, source record, dispatch, delivery and purpose filters;
      source-record filtering considers the latest five hundred requests; pages
      cap at two hundred; invented cursors and incomplete source filters are
      rejected.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#refuses
      a source type given without its id as 400
    origin: none
  - id: BR-CAP-PFM-NOT-019-06
    statement: >-
      Detail includes the originating request, rendering version, ordered
      attempts and up to the oldest two hundred provider events.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/messages.queries.integration.spec.js#answers
      the two hundred oldest, in the order they occurred
    origin: none
  - id: BR-CAP-PFM-NOT-019-07
    statement: >-
      Body access requires sensitive content-read permission; missing stored
      bodies are reported as unavailable rather than returned as empty content.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/storage/communication-objects.integration.spec.js#says
      the content is gone, rather than answering an empty e-mail, when the
      object is not there
    origin: none
  - id: BR-CAP-PFM-NOT-019-08
    statement: >-
      Request detail records purpose, classification, outcome, source, pinned
      version, actor, correlation, key and processing times, plus message counts
      by both states and blocking reasons; source-record requests list newest
      first with a fifty-row limit.
    kind: policy
    enforced_at:
      - api
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/requests.e2e.spec.js#answers
      a blocked request with the reasons, which an accepted one has none of
    origin: none
  - id: BR-CAP-PFM-NOT-019-09
    statement: >-
      The changed-message feed filters by the caller-supplied submitting service
      and returns oldest changes after a cursor, up to two hundred, without
      addresses or content; Further changes reappear with updated state; the
      service filter excludes other service names.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js#leaves
      out another service’s messages in the same tenant
    origin: none
    note: >-
      The source service is a filter supplied by the IAM caller; the module does
      not authenticate an independent service identity here.
  - id: BR-CAP-PFM-NOT-019-10
    statement: >-
      Workspace boundaries apply to requests, messages, content and events;
      records belonging elsewhere are not found.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#shows
      another tenant nothing of it
    origin: none
  - id: BR-CAP-PFM-NOT-019-11
    statement: >-
      Rendered bodies have no age-based storage expiry; request objects expire
      after thirty days, and attachment bytes remain in those objects rather
      than relational records.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: 'services-platform:docs/communications/architecture.md#18.2 The bucket'
    origin: none
  - id: BR-CAP-PFM-NOT-019-12
    statement: >-
      Copy feedback enters message history, but primary-recipient feedback alone
      determines delivery state; a copy bounce suppresses that copy address
      only.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js#does
      not suppress the contact
    origin: >-
      services-platform:docs/communications/architecture.md#16.5 CC and BCC
      (Andrew, 2026-10-09)
scenarios:
  - id: SC-CAP-PFM-NOT-019-01
    given: 'Compliance feedback includes delivery, an image-proxy open and a click.'
    when: Staff inspect message detail.
    then: >-
      Detail separates Platform submission from provider delivery and shows
      delivery time, one automated open and one click.
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#keeps
      the send’s own record of the submission beside the provider’s
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-NOT-018
related:
  - CAP-PFM-NOT-020
  - CAP-PFM-NOT-017
journeys:
  - JNY-PFM-NOT-006
open_questions: []
evidence:
  - >-
    Pinned evidence inventory 1:
    services-platform:docs/communications/architecture.md;
    services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/messages.queries.integration.spec.js;
    services-platform:modules/communications/tests/e2e/requests.e2e.spec.js;
    services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js
  - >-
    Pinned evidence inventory 2:
    services-platform:modules/communications/services/feedback/process-ses-event.ts:rhythm-message;
    services-platform:modules/communications/tests/e2e/messages.e2e.spec.js;
    services-platform:modules/communications/tests/integration/services/storage/communication-objects.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js
controls: [TC-AVL-004, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-002, TC-LOG-003, TC-LOG-005, TC-MON-001, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Inspect communication outcomes and provider feedback

## Purpose

Staff and producing services can distinguish Platform submission activity from provider-reported delivery and engagement.

## How It Works

Platform stores submission attempts separately from provider delivery feedback. Provider correlation tags locate the message and workspace. New events update monotone delivery state, timestamps and counters; duplicates leave them unchanged. Staff list and inspect requests and messages through permission-controlled routes. Body reading requires an additional sensitive permission. Content that is no longer available is reported explicitly. A changed-message feed selects the supplied submitting-service filter without returning addresses or bodies. That filter is not independently verified service identity.

The rule rows define this outcome; the acceptance set identifies existing proof and proposed checks. Implementation evidence alone does not approve the observed details.

## Relationships

Provider submission supplies the dispatch history and correlation needed for feedback.
