---
id: CAP-MBR-MEM-014
title: Alert staff to memberships about to expire
feature: FEAT-MBR-MEM-05
product_area: membership
module: memberships
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes: []
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
intent_sources: []
purpose: >-
  The staff responsible for renewals see, on their console, every membership whose term is about to end without a
  renewal, and the alert goes away when the member renews or is terminated.
outcomes:
  - No membership expires without the renewals team having seen it coming.
  - Alerts reach the staff of the member's own chapter and region before the workspace's.
concepts:
  - CON-MBR-MEM-002
actors:
  - ACT-MBR-006
  - ACT-MBR-012
surfaces:
  - console
  - job
permissions: []
rules:
  - id: BR-CAP-MBR-MEM-014-01
    statement: >-
      A term is expiring when it belongs to a principal membership, is committed or restated, ends between today and
      today plus the horizon (60 days by default, settable from 7 to 180), and has no later term.
    kind: calculation
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#publishes
      nothing for a term that already has a successor
    origin: none
  - id: BR-CAP-MBR-MEM-014-02
    statement: >-
      The horizon is measured from the workspace's business date in its own time zone, both ends included, and moves
      at the workspace's midnight.
    kind: calculation
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/unit/expiring-memberships.spec.ts#starts on the tenant’s business
      date, not the UTC date
    origin: none
  - id: BR-CAP-MBR-MEM-014-03
    statement: >-
      Each expiring membership has one alert, labelled with the member's name (else the membership number), carrying
      the program, the end date, the member and whether they auto-renew, and addressed to the renewals responsibility.
    kind: policy
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#addresses
      the item to membership.renewals, the definition’s default
    origin: none
  - id: BR-CAP-MBR-MEM-014-04
    statement: >-
      An alert is scoped to the membership's units, most specific first, then the workspace.
    kind: policy
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#scopes
      the item by the membership’s chapter and its ancestors, most specific first, then the tenant
    origin: none
  - id: BR-CAP-MBR-MEM-014-05
    statement: >-
      An alert is due at the start of the term's last day in the workspace's time zone and expires thirty days later.
    kind: calculation
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#is due
      at the start of the term’s last day and expires thirty days after it
    origin: none
  - id: BR-CAP-MBR-MEM-014-06
    statement: >-
      A daily sweep publishes each expiring membership, refreshes an existing alert rather than opening another, and
      clears alerts whose term has left the horizon.
    kind: lifecycle
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#refreshes
      the same item’s facts on the next pass rather than opening another
    origin: none
  - id: BR-CAP-MBR-MEM-014-07
    statement: >-
      A renewal resolves the alert as renewed, and a termination as terminated, in the same transaction as the write.
    kind: lifecycle
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#resolves
      the item renewed in the staff renewal’s transaction
    origin: none
  - id: BR-CAP-MBR-MEM-014-08
    statement: >-
      Alerts are shown on the console only, to staff responsibilities only, with no actions, until the portal is
      opened to them.
    kind: policy
    enforced_at: [service]
    must_prove: false
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/unit/expiring-memberships.spec.ts#shows on the console only, to
      staff responsibilities only, with no actions (the portal hold)
    origin: none
  - id: BR-CAP-MBR-MEM-014-09
    statement: >-
      When publishing is switched on, a refusal by Platform's attention service fails the business write it
      accompanies, so a change never commits with a wrong alert beside it; when it is off, nothing is published.
    kind: policy
    enforced_at: [service]
    must_prove: true
    provenance: documented
    source: 'services-membership:docs/attention.md#3. When items are published and resolved'
    origin: none
scenarios:
  - id: SC-CAP-MBR-MEM-014-01
    given: Jo's Regular term ends in 40 days and has no renewal.
    when: The morning sweep runs, and later staff add Jo's renewal.
    then: An alert opens for Jo scoped to Atlanta, its zone and region, then the workspace; the renewal resolves it at once.
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#publishes
      an open item for a membership whose term ends inside the horizon
edge_cases: []
constraints:
  - The sweep publishes in pages of 500 memberships.
depends_on:
  - CAP-MBR-MEM-005
related:
  - CAP-MBR-MEM-013
  - CAP-MBR-MEM-002
journeys:
  - JNY-MBR-MEM-004
open_questions: []
evidence:
  - repository: services-membership
    source_sha: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
    paths:
      - modules/memberships/services/attention/expiring-memberships.ts
      - modules/memberships/services/persistence/expiring-terms.ts
      - modules/memberships/aws/lambda/ATTENTION_SWEEP.ts
      - aws/membership.attention.ts
      - aws/membership.responsibilities.ts
      - lib/attention.ts
      - docs/attention.md
      - docs/migrations/attention/README.md
      - modules/memberships/tests/unit/expiring-memberships.spec.ts
      - modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts
controls: [TC-AVL-002, TC-AVL-003, TC-AVL-004, TC-AZ-001, TC-AZ-003, TC-AZ-004, 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-007, TC-IAM-006, TC-LOG-002, TC-LOG-003, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, 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]
---

# Alert staff to memberships about to expire

## Purpose

The renewals team should never learn about an expiry after the fact. Every membership heading for its end date without a renewal should appear among the team's alerts in time to act, and disappear when the member renews or leaves.

## How It Works

Each morning a sweep looks at every workspace. It finds the principal memberships whose committed term ends within the horizon, 60 days unless the workspace sets otherwise, and that have no later term. Each such membership gets one alert, labelled with the member's name, showing the program, the end date and whether the member auto-renews. The alert goes to whoever holds the renewals responsibility, starting with the member's own chapter and its region, then the workspace. It is due on the term's last day and expires thirty days after. Later sweeps refresh the same alert and clear it once the term leaves the horizon. A renewal or a termination resolves it at once, in the same step as the write. Alerts appear on the console only, with no actions, until the portal is opened to them. Publishing is switched on per environment. This behaviour was built after Andrew's acceptance of Membership and has no record of its own confirming it, so its intent is proposed.

## Relationships

- Depends on `CAP-MBR-MEM-005` because "no later term" and the business date are the standing rules.
- Depends on Platform's responsibilities and attention (`MOD-PFM-ELG`), which store, route and render the alerts.
- Related to `CAP-MBR-MEM-013` because the same "already renewed" rule keeps a term out of a billing run.
