---
id: CAP-MBR-MEM-017
title: Export a roster and let members see their own memberships
feature: FEAT-MBR-MEM-06
product_area: membership
module: memberships
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: partially-observed
supersedes:
  - 'inventory:CAP-MBR-SEC-008'
  - 'inventory:CAP-MBR-SEC-009'
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-membership:README.md#2026-10-18 — Final acceptance'
  - 'services-membership:docs/migrations/scope/README.md#The roster export'
purpose: >-
  An officer can take away the roster of the members they may see, as it stood when they asked, and a portal member
  can see their own memberships.
outcomes:
  - A roster export never holds a member its requester could not see.
  - Members see their own memberships, terms, chapters and status without staff help.
concepts:
  - CON-MBR-MEM-001
actors:
  - ACT-MBR-009
  - ACT-MBR-010
  - ACT-MBR-012
surfaces:
  - portal
  - api
  - job
permissions:
  - key: membership.rosters.export
    where_checked: service
    scope: record
  - key: membership.self.read
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-MBR-MEM-017-01
    statement: >-
      An export captures, when it is asked for, the memberships the requester's scope reaches with the list's filters
      (program, status, unit and units below it, on a date), at most 50,000, and the export runs over those memberships
      only.
    kind: permission
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/unit-scope.integration.spec.ts#captures the ids
      the requester’s set reaches at request time
    origin: none
  - id: BR-CAP-MBR-MEM-017-02
    statement: >-
      Each exported row gives the membership number, the owner, the program, the status on the date and the units on the
      date.
    kind: calculation
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/unit-scope.integration.spec.ts#writes rows for
      the captured memberships only, with their units on the date
    origin: none
  - id: BR-CAP-MBR-MEM-017-03
    statement: >-
      An export is read by the person who asked for it and by workspace-wide holders of the export key, and is not
      found for any other officer.
    kind: permission
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/unit-scope.integration.spec.ts#answers the
      export to the person who asked for it and to staff, and not to another officer
    origin: none
  - id: BR-CAP-MBR-MEM-017-04
    statement: Withdrawing an officer's access later does not recall an export already captured.
    kind: policy
    enforced_at: [service]
    must_prove: false
    provenance: documented
    source: 'services-membership:docs/migrations/scope/README.md#The roster export'
    origin: none
  - id: BR-CAP-MBR-MEM-017-05
    statement: A requester whose scope reaches nobody captures nothing.
    kind: permission
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/services/unit-scope.integration.spec.ts#captures nothing
      for a caller with NONE
    origin: none
  - id: BR-CAP-MBR-MEM-017-06
    statement: >-
      A portal member reads their own memberships, with terms, affiliations, today's status, continuity episodes and
      lifecycle events, through the contact their account is linked to, and cannot use the staff reads.
    kind: permission
    enforced_at: [api, service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/e2e/s07-regional-officer.e2e.spec.ts#refuses Jo the staff read of her own membership: a
      portal member holds no members.read
    origin: none
  - id: BR-CAP-MBR-MEM-017-07
    statement: >-
      Portal members receive Membership's own-memberships key through Membership's built-in portal member policy once
      Platform attaches it to the Portal Member role.
    kind: permission
    enforced_at: [api]
    must_prove: false
    provenance: documented
    source: 'services-membership:docs/migrations/scope/README.md#The keys'
    origin: none
  - id: BR-CAP-MBR-MEM-017-08
    statement: A member's own view carries no Commerce order or order line references.
    kind: policy
    enforced_at: [service]
    must_prove: true
    provenance: unknown
    source: FND-MBR-MEM-057
    origin: none
scenarios:
  - id: SC-CAP-MBR-MEM-017-01
    given: Atlanta's officer asks for the Atlanta roster on a date.
    when: The export job runs after a member's access changes.
    then: The rows hold exactly the members captured at the request, with their units on the date.
    provenance: test
    source: 'services-membership:tests/acceptance/security/MBR-ACC-0460.acceptance.ts#MBR-ACC-0460'
edge_cases: []
constraints:
  - An export captures at most 50,000 memberships.
depends_on:
  - CAP-MBR-MEM-015
related:
  - CAP-MBR-MEM-005
journeys:
  - JNY-MBR-MEM-005
open_questions: []
evidence:
  - repository: services-membership
    source_sha: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
    paths:
      - modules/memberships/services/rosters
      - modules/memberships/services/persistence/roster-exports.ts
      - modules/memberships/services/reads/membership-views.ts
      - modules/memberships/db/roster-export-jobs.table.ts
      - modules/memberships/db/roster-export-job-steps.table.ts
      - modules/memberships/db/roster-export-job-events.table.ts
      - modules/memberships/db/roster-export-rows.table.ts
      - aws/membership.service.ts
      - modules/memberships/tests/integration/services/unit-scope.integration.spec.ts
      - tests/e2e/s07-regional-officer.e2e.spec.ts
      - tests/acceptance/security
  - repository: react-console
    source_sha: baf6dae382d6f41dce976c7f848667ccc37e076f
    paths:
      - acceptance/membership/reports/defects/commerce.md
controls: [TC-AVL-002, TC-AVL-003, TC-AVL-004, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, 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-005, 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-004, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004]
---

# Export a roster and let members see their own memberships

## Purpose

Officers need a list of their members they can take into a meeting or a mailing. Members want to see their own standing without asking staff. Both must show no more than the person asking is entitled to.

## How It Works

An officer asks for an export with the same filters as the list: program, status, unit and the units below it, on a date. At that moment Rhythm captures which memberships the officer's scope reaches, up to 50,000. A background job then writes one row for each, with the number, the owner, the program, the status on the date and the units on the date. Because the capture is the access check, a later change to the officer's access does not recall an export already taken. Only the person who asked, and staff, can read it. The rows are read as pages; a file download is not offered yet. A portal member signed in to the association's portal sees every one of their own memberships, with terms, chapters, status and history of events, and nothing of anyone else's. Portal members get this access only when Platform attaches Membership's portal policy to its Portal Member role. The product should keep Commerce order references out of the member's own view; today they appear.

## Relationships

- Depends on `CAP-MBR-MEM-015` because the export captures exactly the officer's scope.
- Depends on Platform security (`MOD-PFM-SEC`) for the Portal Member role and on Platform's durable jobs (`MOD-PFM-OPS`) for the export job.
