---
id: WF-MBR-MEM-001
title: Record a membership and its first term
trigger: >-
  Staff submit a first membership period with an explanation and no Commerce
  order.
product_area: membership
module: memberships
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-MBR-MEM-001
  - CAP-MBR-MEM-006
moves:
  - STATE-MBR-MEM-001
  - STATE-MBR-MEM-003
crosses:
  - SVC-MBR-MEM-memberships
  - SVC-MBR-MEM-events
  - SVC-MBR-MEM-history
idempotency: >-
  An optional request key returns the recorded response. Without that key,
  repeating successful creation meets the existing principal identity and is
  refused.
concurrency: >-
  Creation locks the owner before checking identity and exclusive holdings.
  Competing creation requests serialize, with database uniqueness providing a
  second defence.
hops:
  - 'n': 1
    actor_or_service: Membership staff through the API
    action: 'Validate the submitted owner, program, identity binding, type and package.'
    anchor: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:createMembership
    seam: sync
    on_failure: Return a validation error identifying the unacceptable field.
    left_inconsistent: No partial local change remains.
    observer: Caller-visible API result
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:createMembership,
      requireTypeOf, requirePackageOf
  - 'n': 2
    actor_or_service: Memberships service
    action: >-
      Acquire the owner lock, reject duplicate identity or conflicting exclusive
      coverage, and calculate the period.
    anchor: >-
      services-membership:modules/memberships/services/persistence/memberships.ts:lockOwner
    seam: sync
    on_failure: Return the identity or coverage conflict and roll back the write.
    left_inconsistent: No partial local change remains.
    observer: Caller-visible API result
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:ensureExclusivity,
      datesOf
  - 'n': 3
    actor_or_service: Memberships service
    action: Create the holding and allocate its configured membership number.
    anchor: >-
      services-membership:modules/memberships/services/membership-numbers.ts:nextMembershipNumber
    seam: sync
    on_failure: Reject unavailable owner numbering and roll back the membership write.
    left_inconsistent: 'Business records roll back, but an allocated sequence value can be lost.'
    observer: Caller-visible API result
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/membership-numbers.ts:nextMembershipNumber
  - 'n': 4
    actor_or_service: Memberships service
    action: >-
      Insert the committed period, record enrolment and its business event, and
      apply any enabled alert resolution.
    anchor: >-
      services-membership:modules/memberships/services/operations/term-writes.ts:writeCommittedTerm
    seam: sync
    on_failure: Overlap or enabled attention failure aborts all transaction effects.
    left_inconsistent: No partial local change remains.
    observer: Caller-visible API result
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/term-writes.ts:writeCommittedTerm
  - 'n': 5
    actor_or_service: Memberships service
    action: Validate and insert the chosen units for an affiliation-bound program.
    anchor: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:writeTermAffiliations
    seam: sync
    on_failure: Report the unit-rule violation and roll back dependent writes.
    left_inconsistent: No partial local change remains.
    observer: Caller-visible API result
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:writeTermAffiliations
  - 'n': 6
    actor_or_service: Memberships service
    action: >-
      Apply package grants, synchronize organizational children and recompute
      cached facts.
    anchor: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:createMembership
    seam: sync
    on_failure: An error aborts the transaction and reverses its writes.
    left_inconsistent: No partial local change remains.
    observer: Caller-visible API result
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/grants/apply-grants.ts:applyPackageGrants;
      services/operations/term-writes.ts:finishMembershipWrite
  - 'n': 7
    actor_or_service: Database at commit
    action: >-
      Capture temporal versions and attributed revisions in the transaction;
      reject unacceptable attribution at commit.
    anchor: >-
      services-membership:db/migrations/20261018044117_commerce_participant.sql:memberships_versions_capture
    seam: sync
    on_failure: 'Commit rejects the transaction, preserving no business changes.'
    left_inconsistent: No partial local change remains.
    observer: Caller-visible API result
    provenance: test
    source: >-
      services-membership:modules/history/tests/integration/membership-history.integration.spec.ts#refuses
      at commit a write that names no source and no actor
  - 'n': 8
    actor_or_service: Change-events dispatcher
    action: >-
      Dispatch committed business events to configured handlers; revision
      creation has already completed.
    anchor: 'services-membership:modules/events/aws/logic/change-events.lambda.ts'
    seam: event
    on_failure: >-
      Configured receiver failures retry through the queue and reach dead-letter
      handling when exhausted.
    left_inconsistent: >-
      The holding is committed before its configured receiver reactions
      complete.
    observer: Queue dead-letter alarm
    provenance: observed
    source: 'services-membership:aws/membership.change-events.ts'
branches:
  - >-
    Organization entry derives eligible children before the same transaction
    commits.
  - A request cannot attach units to a program that has no affiliation binding.
waits:
  - >-
    A minute dispatch schedule supplies polling cadence; successful receiver
    work can take longer.
evidence:
  - >-
    services-membership at 5994c270:
    modules/memberships/services/operations/staff-operations.ts, term-writes.ts;
    services/grants/apply-grants.ts;
    services/affiliations/affiliation-operations.ts;
    aws/membership.idempotency.ts
controls: [TC-AVL-002, TC-AVL-003, TC-AVL-004, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-007, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Record a membership and its first term

## What It Does

Staff establish a holding and its first period without a purchase. The transaction also records selected units, package grants and any organizational children.

## The Flow

```mermaid
flowchart TD
  H1["Validate the submitted owner, program, identity binding, type and package"]
  H2["Acquire the owner lock, reject duplicate identity or conflicting exclusive coverage, and calculate the period"]
  H1 --> H2
  H3["Create the holding and allocate its configured membership number"]
  H2 --> H3
  H4["Insert the committed period, record enrolment and its business event, and apply any enabled alert resolution"]
  H3 --> H4
  H5["Validate and insert the chosen units for an affiliation-bound program"]
  H4 --> H5
  H6["Apply package grants, synchronize organizational children and recompute cached facts"]
  H5 --> H6
  H7["Capture temporal versions and attributed revisions in the transaction; reject unacceptable attribution at commit"]
  H6 --> H7
  H8["Dispatch committed business events to configured handlers; revision creation has already completed"]
  H7 --> H8
```

## When Something Fails

A validation or write failure rolls back the membership transaction. Number allocation can consume a sequence value even after rollback. Revisions settle at commit; only delivery to configured event receivers follows afterward and can require retry.

## Open Design Questions

Custom values have no entry path, and later preference, join-date and owner changes lack staff operations.
