---
id: CAP-PFM-NOT-018
title: Send association e-mail with retry and capacity control
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#2026-10-09 — Final
    acceptance
purpose: >-
  Platform submits messages to the provider while managing retries, ambiguity
  and separate traffic budgets.
outcomes:
  - >-
    Dispatch sends eligible association e-mail through capacity-controlled
    lanes.
  - >-
    Ambiguous attempts can become eligible for another submission after the
    recovery interval.
concepts:
  - CON-PFM-NOT-005
actors:
  - ACT-PFM-031
  - ACT-PFM-028
surfaces:
  - job
  - api
preconditions: []
postconditions: []
permissions:
  - key: platform.communication-messages.update
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-PFM-NOT-018-01
    statement: >-
      Minute scheduling and post-commit nudges run oldest-first workspace
      dispatch under short claims; only pointers reach the queue; acknowledged
      rows are deleted, refused rows remain until claim expiry, concurrent
      claims stay exclusive and time exhaustion stops before the next workspace.
    kind: policy
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/dispatch-communications.integration.spec.js#leaves
      the refused row and deletes the accepted one
    origin: none
  - id: BR-CAP-PFM-NOT-018-02
    statement: >-
      Request processing handles one item at a time and skips final messages on
      redelivery; Potentially in-flight submissions wait through the ambiguity
      interval before the separate resubmission recovery applies.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/process-communication-request.integration.spec.js#sends
      nothing a second time
    origin: >-
      services-platform:docs/communications/architecture.md#2026-10-09 — Final
      acceptance
  - id: BR-CAP-PFM-NOT-018-03
    statement: >-
      Throttling stops the remaining sends for that receive and retains their
      draft state until backoff retry; On receive eight, refused sends fail as
      exhausted, untouched messages are attempted and the item is dead-lettered.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/process-communication-request.integration.spec.js#stops
      the rest of the request for this receive rather than pressing an unwell
      provider
    origin: none
  - id: BR-CAP-PFM-NOT-018-04
    statement: >-
      Operator redrive reoffers exhausted messages only; provider-accepted and
      permanently refused messages remain final.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/process-communication-request.integration.spec.js#never
      offers one the provider already took: the request is settled, not re-sent
    origin: none
  - id: BR-CAP-PFM-NOT-018-05
    statement: >-
      A final pre-send check expires overdue messages without an attempt and
      withholds newly suppressed or reserved-domain recipients with a reason.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js#withholds
      a message whose recipient was suppressed since acceptance, state and
      reason written together
    origin: none
  - id: BR-CAP-PFM-NOT-018-06
    statement: >-
      An uncertain submission waits through the ambiguity interval before its
      unknown attempt closes and a new claim can submit again; Provider send
      feedback can complete it during the wait.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js#claims
      it again once the ambiguity window has passed, the unknown attempt closed
    origin: none
  - id: BR-CAP-PFM-NOT-018-07
    statement: >-
      Message-update permission permits cancellation only while a message is
      draft; Withheld, cancelled and submitted messages cannot be cancelled.
    kind: lifecycle
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#refuses
      cancelling a message that was withheld rather than drafted as 400
    origin: none
  - id: BR-CAP-PFM-NOT-018-08
    statement: >-
      Purpose classification selects a distinct Marketing lane rather than
      trusting request input; Marketing traffic cannot enter the transactional
      queue or occupy its front positions.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/marketing-lane.integration.spec.js#leaves
      the marketing row for the next run, never publishing it to the other lane
    origin: none
  - id: BR-CAP-PFM-NOT-018-09
    statement: >-
      Transactional traffic reserves forty percent of account rate and can
      borrow unused Marketing capacity; Marketing is capped at sixty percent
      without borrowing; a lane without a previously read rate remains
      unlimited.
    kind: calculation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/unit/services/dispatch/lane-bucket.spec.js#never
      lets the marketing lane borrow the transactional lane’s reserved tokens
    origin: none
  - id: BR-CAP-PFM-NOT-018-10
    statement: >-
      Rate-budget waits remain inside the invocation when time permits,
      otherwise returning work to its lane with a delay that does not spend a
      retry; Completed sends are skipped on return.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/marketing-lane.integration.spec.js#requeues
      the item rather than giving it back to the queue to spend a receive
    origin: none
  - id: BR-CAP-PFM-NOT-018-11
    statement: >-
      Marketing pauses at ninety percent of the twenty-four-hour account quota
      and resumes below that threshold; Transactional processing continues.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/lane-budgets.integration.spec.js#pauses
      the marketing lane at exactly 90 %
    origin: none
  - id: BR-CAP-PFM-NOT-018-12
    statement: >-
      Marketing requests require subscription classification and unsubscribe
      targets; other requests prohibit them; Delivery adds List-Unsubscribe and
      enables one-click unsubscribe for HTTPS targets.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/marketing-lane.integration.spec.js#refuses
      a marketing request without a subscription type, with the library’s
      message
    origin: none
  - id: BR-CAP-PFM-NOT-018-13
    statement: Current lane scheduling does not guarantee equal workspace shares.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: >-
      services-platform:docs/communications/architecture.md#21.3 The 24-hour
      quota brake
    origin: none
    note: >-
      Left for Andrew's decision by the as-built record; see the open question
      on fairness.
  - id: BR-CAP-PFM-NOT-018-14
    statement: >-
      Provider submission includes the configuration set and correlation tags
      used to locate the message and workspace in feedback.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#sends
      it with the configuration set and the tags that let feedback find the
      tenant and message
    origin: none
scenarios:
  - id: SC-CAP-PFM-NOT-018-01
    given: >-
      Marketing processes a forty-thousand-member newsletter when transactional
      compliance mail arrives.
    when: The two classified consumers use their lane budgets.
    then: >-
      Compliance uses reserved transactional capacity while Marketing waits for
      its own available tokens.
    provenance: inferred
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/marketing-lane.integration.spec.js#sends
      transactional mail from its own tokens while marketing is spent
    note: >-
      The tests prove lane separation and reserved token behavior; the large
      campaign is an illustrative scale rather than the tested recipient count.
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-NOT-017
related:
  - CAP-PFM-NOT-019
  - CAP-PFM-NOT-020
journeys:
  - JNY-PFM-NOT-006
open_questions:
  - OQ-PFM-NOT-005
evidence:
  - >-
    Pinned evidence inventory 1:
    services-platform:docs/communications/architecture.md;
    services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js;
    services-platform:modules/communications/tests/unit/services/dispatch/lane-bucket.spec.js
  - >-
    Pinned evidence inventory 2:
    services-platform:modules/communications/tests/integration/services/dispatch/dispatch-communications.integration.spec.js;
    services-platform:modules/communications/tests/e2e/messages.e2e.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/lane-budgets.integration.spec.js
  - >-
    Pinned evidence inventory 3:
    services-platform:modules/communications/tests/integration/services/dispatch/process-communication-request.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/dispatch/marketing-lane.integration.spec.js;
    services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js
controls: [TC-AVL-001, 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-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-004, 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-PRV-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---

# Send association e-mail with retry and capacity control

## Purpose

Platform submits messages to the provider while managing retries, ambiguity and separate traffic budgets.

## How It Works

Dispatch transfers committed communication pointers onto the appropriate queue. The consumer resolves the request and processes individual messages. Immediately before submission it rechecks expiry and recipient protection. Provider throttling applies backoff and eventually exhausts affected attempts. Uncertain provider submission waits before recovery and can be submitted again after the ambiguity interval. Purpose classification separates transactional and Marketing lanes. Rate budgets and quota braking delay bulk traffic while reserving transactional capacity. Current scheduling does not establish fair shares among workspaces.

The product should define the acceptable ambiguity recovery and workspace fairness policy before promising delivery guarantees.

## Relationships

Accepted communication requests supply the recipient messages consumed by delivery.
