---
id: CAP-PFM-NOT-015
title: Store notification preferences and channel configuration
feature: FEAT-PFM-NOT-06
product_area: platform
module: notifications
revision: 1
status: draft
product_intent: proposed
implementation_evidence: partially-observed
supersedes: []
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-platform:docs/proposals/notifications/23-decisions.md#D-05'
  - 'services-platform:docs/proposals/notifications/23-decisions.md#D-10'
purpose: >-
  Platform stores future delivery choices and their validation, without
  representing them as active runtime preferences.
outcomes:
  - 'Stored settings validate category, definition and channel choices.'
  - >-
    The implemented runtime does not yet consume these future delivery
    preferences.
concepts:
  - CON-PFM-NOT-003
actors:
  - ACT-PFM-027
  - ACT-PFM-028
surfaces:
  - console
  - api
preconditions: []
postconditions: []
permissions: []
rules:
  - id: BR-CAP-PFM-NOT-015-01
    statement: >-
      Personal preferences store channel switches and digest selection for each
      category, with optional overrides for individual definitions.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: >-
      services-platform:docs/proposals/notifications/23-decisions.md#D-05. The
      preference grain is the category, with per-definition exceptions
    origin: 'services-platform:docs/proposals/notifications/23-decisions.md#D-05'
  - id: BR-CAP-PFM-NOT-015-02
    statement: >-
      Preferences require known categories or definitions and valid switches;
      console cannot be disabled, mandatory e-mail cannot be disabled when
      supported, a nonempty channel-configurable override includes an enabled
      switch, and per-definition daily digest requires digestibility, while
      empty overrides and category-level digest selection remain valid.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/notifications/services/persistence/preferences.ts:refuseInvalid
    origin: none
  - id: BR-CAP-PFM-NOT-015-03
    statement: >-
      Preference and setting writes use optimistic versions; initial creation
      needs none, while subsequent writes require the version read; omitted or
      stale versions reject the entire update.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/preferences.integration.spec.js#refuses
      a stale version with a 409 and leaves the choice as it was
    origin: none
  - id: BR-CAP-PFM-NOT-015-04
    statement: >-
      Workspace channel defaults enable console and e-mail and disable chat and
      text; console cannot be disabled, connections apply only to connected
      channels, and text limits use whole segment counts.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/channel-settings.integration.spec.js#answers
      the defaults for a tenant that wrote nothing: the console and e-mail on,
      chat and SMS off
    origin: none
  - id: BR-CAP-PFM-NOT-015-05
    statement: >-
      Workspace definition settings cannot disable mandatory notices, turn them
      into another requirement or narrow them below e-mail; Allowed channels are
      supported by the definition and include console.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/definition-settings.integration.spec.js#refuses
      to turn a mandatory definition off
    origin: none
  - id: BR-CAP-PFM-NOT-015-06
    statement: >-
      Wording drafts advance numbered versions with one active override per
      definition, channel and locale; Publication checks field use and rendering
      limits; deactivation removes the override, while the current pipeline does
      not apply these overrides.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/template-overrides.integration.spec.js#leaves
      exactly one active version per definition, channel and locale
    origin: none
  - id: BR-CAP-PFM-NOT-015-07
    statement: >-
      Successful workspace channel, definition-setting and wording changes
      record actor and before-and-after audit data; Rejected updates produce no
      audit entry.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/channel-settings.integration.spec.js#audits
      the first write as a create and an update with the before and after
    origin: none
  - id: BR-CAP-PFM-NOT-015-08
    statement: >-
      Quiet-hour storage requires unequal start and end times and distinct
      weekdays numbered one to seven; Digest delivery time defaults to 08:00 and
      accepts only valid times of day.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/user-settings.integration.spec.js#refuses
      quiet hours that begin and end together
    origin: none
  - id: BR-CAP-PFM-NOT-015-09
    statement: >-
      A person holds one current identity per chat or text channel, and a phone
      number identifies one person; Pending identities can become active,
      unreachable, opted out or revoked; revoked is terminal.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/identities.integration.spec.js#refuses
      a second current identity on a channel for the same person
    origin: none
  - id: BR-CAP-PFM-NOT-015-10
    statement: >-
      Phone challenges retain a code digest, allow one open challenge per number
      and expire; Five incorrect submissions exhaust a challenge.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/identities.integration.spec.js#spends
      the challenge after five wrong codes, refusing even the right one
    origin: none
  - id: BR-CAP-PFM-NOT-015-11
    statement: >-
      Text sending reserves segments against the workspace's UTC calendar-month
      allowance; Atomic reservation admits only sends that fit; zero admits
      none, and the first ceiling refusal time is retained.
    kind: calculation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/sms-usages.integration.spec.js#lets
      only the sends that fit through when they race at the edge
    origin: none
  - id: BR-CAP-PFM-NOT-015-12
    statement: >-
      Digest records identify one recipient, category and window; Opening is
      idempotent, contributions increment the count while open, and settlement
      happens once.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/digests.integration.spec.js#answers
      the digest already open for the same person, category and window
    origin: none
  - id: BR-CAP-PFM-NOT-015-13
    statement: >-
      Stored preferences have no user interface or runtime effect yet; All
      definitions are treated as enabled, and individual delivery remains
      console-only.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-platform:modules/notifications/services/orchestration/orchestrate-request.ts:definition_disabled
    origin: none
scenarios:
  - id: SC-CAP-PFM-NOT-015-01
    given: >-
      In a future preference release, Jane wants assignment e-mail and console
      delivery while suppressing optional job notices.
    when: Jane chooses both assignment routes and disables her jobs category.
    then: >-
      The proposed outcome delivers assignments on those routes and suppresses
      optional jobs, while mandatory supported routes remain enabled.
    provenance: proposed
    source: >-
      services-platform:docs/proposals/notifications/23-decisions.md#D-05. The
      preference grain is the category, with per-definition exceptions
    note: >-
      This scenario describes a future preference release; stored choices do not
      alter current delivery.
edge_cases: []
constraints: []
depends_on:
  - CAP-PFM-NOT-001
related:
  - CAP-PFM-NOT-007
  - CAP-PFM-NOT-004
journeys: []
open_questions: []
evidence:
  - >-
    Pinned evidence inventory 1:
    services-platform:docs/proposals/notifications/23-decisions.md;
    services-platform:modules/notifications/tests/integration/services/persistence/preferences.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/channel-settings.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/definition-settings.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/template-overrides.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/user-settings.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/identities.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/sms-usages.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/digests.integration.spec.js;
    services-platform:modules/notifications/services/orchestration/orchestrate-request.ts
controls: [TC-AVL-005, 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-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, 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]
---

# Store notification preferences and channel configuration

## Purpose

Platform stores future delivery choices and their validation, without representing them as active runtime preferences.

## How It Works

Platform persistence accepts category preferences and definition exceptions for future channel selection. Workspace settings and wording are stored separately from personal choices. Validation preserves mandatory and console requirements. Version checks prevent overwriting an update based on stale state. Successful workspace changes append audit information. Identity and phone-challenge records track future reachable destinations. Text reservations and digest records provide accounting primitives. The current pipeline and console do not expose or apply these stored choices.

Stored configuration is evidence of persistence behavior; future channel selection still requires a product plan and implementation.

## Relationships

Registration supplies the event contract that later work validates and renders.
