---
id: ATS-CRM-ENT-007
scope: CAP-CRM-ENT-007
product_area: crm
module: entities
revision: 1
status: proposed
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-CRM-ENT-007-01
    title: One preference per communication type
    level: api
    proves:
      - BR-CAP-CRM-ENT-007-02
    preconditions: No additional setup is required.
    steps: Save two preferences of one type.
    expected: Rejected naming the second; no record changes.
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-787
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      a communication type listed twice, naming the second entry
    category: failure
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
        a communication type listed twice, naming the second entry
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
        a communication type listed twice, naming the second entry
    test_anchors:
      - >-
        services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
        a communication type listed twice, naming the second entry
      - >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a second preference for one communication type by
        contact_communication_preferences_type_uq
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-007-02
    title: Subscription addresses canonical and valid
    level: api
    proves:
      - BR-CAP-CRM-ENT-007-03
    preconditions: No additional setup is required.
    steps: >-
      Write an address in capitals, with whitespace and not shaped like an
      address.
    expected: Each rejected by the canonical and format checks.
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-787
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
      a subscription value that is not shaped like an address by the domain’s
      email_address_format_check
    category: validation
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription value that is not shaped like an address by the domain’s
        email_address_format_check
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription value that is not shaped like an address by the domain’s
        email_address_format_check
    test_anchors:
      - >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription address with an uppercase letter by the domain’s
        email_address_canonical_check
      - >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription value that is not shaped like an address by the domain’s
        email_address_format_check
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-007-03
    title: Subscription status is a closed list
    level: api
    proves:
      - BR-CAP-CRM-ENT-007-04
    preconditions: No additional setup is required.
    steps: Write status "maybe"; omit enabled.
    expected: Rejected each time.
    negative: true
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-787
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
      a subscription status outside the closed list by
      contact_communication_preferences_status_check
    category: validation
    action_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription status outside the closed list by
        contact_communication_preferences_status_check
    assertion_evidence:
      provenance: test
      source: >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription status outside the closed list by
        contact_communication_preferences_status_check
    test_anchors:
      - >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a subscription status outside the closed list by
        contact_communication_preferences_status_check
      - >-
        services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
        a row that does not say whether the preference is enabled, naming the
        column
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-007-04
    title: Restrictions are set from the console and displayed
    level: browser
    proves:
      - BR-CAP-CRM-ENT-007-01
    preconditions: Prepare a contact.
    steps: >-
      Toggle the three restrictions on the communication tab and add preference
      rows.
    expected: 'The values land, default false, and the header displays them.'
    negative: false
    data_needs: No additional fixtures.
    bound_to:
      - TST-CRM-ENT-774
    provenance: test
    source: >-
      react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0025.spec.ts#CRM-ACC-0025
    category: rule
    action_evidence:
      provenance: test
      source: >-
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0025.spec.ts#CRM-ACC-0025
    assertion_evidence:
      provenance: test
      source: >-
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0025.spec.ts#CRM-ACC-0025
    test_anchors:
      - >-
        react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0025.spec.ts#CRM-ACC-0025
    binding_scope: >-
      contributions: the cited executable assertions collectively support only
      the observations they contain; additional matrix and journey checks remain
      proposed.
  - id: ATS-CRM-ENT-007-05
    title: Retain an unrecognized communication identifier
    category: edge
    level: api
    proves:
      - BR-CAP-CRM-ENT-007-05
    preconditions: >-
      A valid person exists and a preference uses an identifier absent from the
      configured communication catalogue.
    steps: Save that preference and read the person.
    expected: The stored preference still names the submitted identifier.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/services/persistence/contacts.ts:communicationPreferenceProblems
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/services/persistence/contacts.ts:communicationPreferenceProblems
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/services/persistence/contacts.ts:communicationPreferenceProblems
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-007-06
    title: Read communication choices after creation
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-001-01
      - BR-CAP-CRM-ENT-007-01
    preconditions: A person request supplies one communication restriction.
    steps: Create the person and read its communication choices.
    expected: >-
      The supplied restriction is visible and the unspecified restrictions use
      their creation defaults.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/e2e/contacts-names.e2e.spec.js#answers
      400 for a contact with neither a first nor a last name, naming the rule;
      services-crm:modules/entities/db/contacts.table.ts:do_not_mail
    exercises:
      - CAP-CRM-ENT-001
      - CAP-CRM-ENT-007
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contacts-names.e2e.spec.js#answers
        400 for a contact with neither a first nor a last name, naming the rule;
        services-crm:modules/entities/db/contacts.table.ts:do_not_mail
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contacts-names.e2e.spec.js#answers
        400 for a contact with neither a first nor a last name, naming the rule;
        services-crm:modules/entities/db/contacts.table.ts:do_not_mail
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-007-07
    title: Show a stored restriction to a record reader
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-003-06
      - BR-CAP-CRM-ENT-007-01
    preconditions: A person has a recorded mail restriction and a reader can open the record.
    steps: Open the person’s record after the restriction is saved.
    expected: >-
      The stored choice is visible to the reader without offering unauthorized
      edits.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only
      the fields `?fields=` named, with the id;
      services-crm:modules/entities/db/contacts.table.ts:do_not_mail
    exercises:
      - CAP-CRM-ENT-003
      - CAP-CRM-ENT-007
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers
        only the fields `?fields=` named, with the id;
        services-crm:modules/entities/db/contacts.table.ts:do_not_mail
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers
        only the fields `?fields=` named, with the id;
        services-crm:modules/entities/db/contacts.table.ts:do_not_mail
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-007-08
    title: Keep storage proof separate from notice-recipient selection
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-ENT-007-06
      - BR-CAP-CRM-ENT-012-08
    preconditions: >-
      A current notification-role holder has a primary address and recorded
      communication restrictions.
    steps: Read the stored choices and compose the compliance notice’s recipients.
    expected: >-
      The choices remain stored; this module’s recipient query is assessed by
      the notice rule and does not establish sender enforcement.
    negative: false
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2.
      Scope;
      services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-reads.integration.spec.js#leaves
      out the contact whose addresses sit past the primary slot, no secondary
      address standing in
    exercises:
      - CAP-CRM-ENT-007
      - CAP-CRM-ENT-012
    workflows: []
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2.
        Scope;
        services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-reads.integration.spec.js#leaves
        out the contact whose addresses sit past the primary slot, no secondary
        address standing in
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2.
        Scope;
        services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-reads.integration.spec.js#leaves
        out the contact whose addresses sit past the primary slot, no secondary
        address standing in
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
  - id: ATS-CRM-ENT-007-09
    title: Exercise every declared authority independently
    category: permission
    level: api
    proves:
      - BR-CAP-CRM-ENT-003-07
      - BR-CAP-CRM-ENT-003-09
    preconditions: >-
      An eligible record and configuration fixture exist for each operation; the
      caller can reach only the run’s workspace.
    steps: >-
      For each declared permission, execute its matching operation with that
      permission, then without it while retaining the other required grants.
      Read affected records after every refused mutation.
    expected: >-
      The authorized case reaches the operation; the missing-grant case is
      refused and preserves saved data. Record access alone does not authorize
      the other declared actions.
    negative: true
    data_needs: Run-owned records and configuration in an isolated workspace.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:aws/crm.service.ts:ApplicationPermissions;
      services-crm:modules/entities/aws/api/entities.routes.ts
    permission_keys:
      - crm.contacts.update
      - crm.contacts.read
    test_anchors: []
    action_evidence:
      provenance: proposed
      source: >-
        services-crm:aws/crm.service.ts:ApplicationPermissions;
        services-crm:modules/entities/aws/api/entities.routes.ts
    assertion_evidence:
      provenance: proposed
      source: >-
        services-crm:aws/crm.service.ts:ApplicationPermissions;
        services-crm:modules/entities/aws/api/entities.routes.ts
    binding_scope: 'unbound: the detailed scenario still needs executable proof.'
evidence:
  - >-
    services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
    a communication type listed twice, naming the second entry
  - >-
    services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
    a second preference for one communication type by
    contact_communication_preferences_type_uq
  - >-
    services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
    a subscription value that is not shaped like an address by the domain’s
    email_address_format_check
  - >-
    services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
    a subscription address with an uppercase letter by the domain’s
    email_address_canonical_check
  - >-
    services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
    a subscription status outside the closed list by
    contact_communication_preferences_status_check
  - >-
    services-crm:modules/entities/tests/integration/db/contact-communication-preferences.table.integration.spec.js#refuses
    a row that does not say whether the preference is enabled, naming the column
  - >-
    react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0025.spec.ts#CRM-ACC-0025
  - >-
    services-crm:modules/entities/services/persistence/contacts.ts:communicationPreferenceProblems
  - >-
    services-crm:modules/entities/tests/e2e/contacts-names.e2e.spec.js#answers
    400 for a contact with neither a first nor a last name, naming the rule;
    services-crm:modules/entities/db/contacts.table.ts:do_not_mail
  - >-
    services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers only
    the fields `?fields=` named, with the id;
    services-crm:modules/entities/db/contacts.table.ts:do_not_mail
  - >-
    services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2.
    Scope;
    services-crm:modules/entities/tests/integration/services/organizations/compliance/compliance-reads.integration.spec.js#leaves
    out the contact whose addresses sit past the primary slot, no secondary
    address standing in
  - >-
    services-crm:aws/crm.service.ts:ApplicationPermissions;
    services-crm:modules/entities/aws/api/entities.routes.ts
coverage:
  rule: covered
  transition: 'gap: FND-CRM-ENT-055'
  validation: covered
  permission: covered
  edge: covered
  failure: covered
  boundary: 'gap: FND-CRM-ENT-055'
  concurrency: 'gap: FND-CRM-ENT-055'
  invariant: 'gap: FND-CRM-ENT-055'
  interaction: covered
previous_approval:
  revision: 1
  approved_by: Andrew
  approved_at: '2026-10-22T00:00:00.000Z'
coverage_notes:
  transition:
    reason: >-
      No detailed transition scenario currently specifies this aspect; the
      coverage finding records the required follow-through.
    provenance: unknown
    source: FND-CRM-ENT-055
  boundary:
    reason: >-
      No detailed boundary scenario currently specifies this aspect; the
      coverage finding records the required follow-through.
    provenance: unknown
    source: FND-CRM-ENT-055
  concurrency:
    reason: >-
      No detailed concurrency scenario currently specifies this aspect; the
      coverage finding records the required follow-through.
    provenance: unknown
    source: FND-CRM-ENT-055
  invariant:
    reason: >-
      No detailed invariant scenario currently specifies this aspect; the
      coverage finding records the required follow-through.
    provenance: unknown
    source: FND-CRM-ENT-055
coverage_findings:
  - FND-CRM-ENT-055
  - FND-CRM-ENT-075
permission_coverage:
  - key: crm.contacts.update
    where_checked: api
    scenario: ATS-CRM-ENT-007-09
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-007-record-how-a-person-wants-to-be-contacted.md
  - key: crm.contacts.read
    where_checked: api
    scenario: ATS-CRM-ENT-007-09
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-007-record-how-a-person-wants-to-be-contacted.md
related_coverage:
  - capability: CAP-CRM-ENT-001
    scenarios:
      - ATS-CRM-ENT-007-06
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-007-record-how-a-person-wants-to-be-contacted.md:related
  - capability: CAP-CRM-ENT-003
    scenarios:
      - ATS-CRM-ENT-007-07
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-007-record-how-a-person-wants-to-be-contacted.md:related
  - capability: CAP-CRM-ENT-012
    scenarios:
      - ATS-CRM-ENT-007-08
    provenance: documented
    source: >-
      rhythm-v2-docs:specs/crm/entities/planning/capabilities/CAP-CRM-ENT-007-record-how-a-person-wants-to-be-contacted.md:related
examination_evidence:
  provenance: documented
  source: 'rhythm-v2-docs:specs/crm/entities/reports/EXAMINATION-2026-10-23.md'
  heading_conflict: FND-CRM-ENT-075
---

# Capture a person’s communication choices
## Coverage

The category map separates specified cases from recorded gaps. A proposed case describes proof to implement; a binding identifies the assertions supplied by an existing executable file. The permission and transition ledgers record the individual checks needed for the capability.

| Required rule | Scenario or finding |
|---|---|
| `BR-CAP-CRM-ENT-007-02` | `ATS-CRM-ENT-007-01` |
| `BR-CAP-CRM-ENT-007-03` | `ATS-CRM-ENT-007-02` |
| `BR-CAP-CRM-ENT-007-04` | `ATS-CRM-ENT-007-03` |

## Interactions With Related Capabilities

The interaction scenarios exercise establish a person’s CRM record; locate people and organizations; review organization role compliance and prepare notices. They change one part of a prepared record and inspect the dependent outcome. Unbound interactions remain proposed rather than implying an existing end-to-end implementation.

## Source Evidence

The frontmatter pins the repositories and identifies the source anchors for each scenario. Bindings record inspected assertions, while unbound cases remain proposed. Source inspection supplies no execution result or product approval.
