Privacy & Data Minimization Matrix

20 min read•docs/PRIVACY-ACCESS-MATRIX.md•

Privacy / access matrix (T1.11)

Status: ready for test (implement attestation 2026-08-12)
Purpose: Lock role × screen × data-class access for internal CX Orbit users. This is the policy matrix. T5.5 assigns roles; T5.6 enforces the matrix in backend + UI. Neither is done.
Product contract: SCREENS-SPEC.md jobs. As-built P0 layouts: P0-WIREFRAMES.md (T1.8). Privacy classes: fixtures/events/README.md (T1.12).

This is a documentation/spec lock. It does not change T2.10 / T3.2 / T3.3 / T3.4 / T3.8 pages or APIs.


1. How to read this doc

1.1 What this task is / is not

This task (T1.11) Later — do not treat as done
Role × screen × data-class policy T5.5 — users/teams/roles admin (SET-01/02). Status: ready.
PII classes labeled; support 360 minimum necessary; export rules T5.6 — field-level matrix UI + backend enforce (SET-03). Blocked by T1.11 + T5.5.
Payment-secrets policy for support timeline (this file §5) T1.14 — browser consent, anonymous ID storage, data-minimization. Policy: BROWSER-CONSENT-POLICY.md. SDK implementation is T2.13 (P1). This matrix does not own cookie rules.
Aligns with T1.14 / T4.10 path T4.10 — security review checklist SECURITY-REVIEW-T4.10.md (implement attestation 2026-08-12). Named human sign-off residual. Enforcement remains T5.6.

T5.6 DoD: access dimensions view / export / admin / sensitive case; PII, finance, ORS/SRS gated by role; backend enforce (UI reflect-only). This file is the matrix T5.6 must implement. Until then, as-built auth is a shared staff token (P0-WIREFRAMES.md §3; ADR 0001).

1.2 Evidence basis

Artifact What it contributes
SCREENS-SPEC.md Primary users per screen; Capability 15 role shortcuts
P0-WIREFRAMES.md Four shipped P0 screens 1:1 with T2.10 / T3.2 / T3.3 / T3.4+T3.8
T1.12 fixtures/events/README.md + contracts.yaml Property-level privacy classes
DATA-MODEL.md §1 / §9.10 Restricted ORS/SRS / free-text; identifier minimization
SOURCE-EVENT-MAPPING.md Event-level restricted on FDB-* (not in the T1.12 12-event pack)
PRD C360-03 / C360-05 / C360-06 / SET-03 Timeline vs raw event; operational finance; ORS/SRS gating; export mask
ADR 0001 Role tags pm / ops / support / matching / leadership / admin

Residual: named security/product sign-off of this matrix (same class as T1.8 named PM/ops interview). T4.10 is that review. Do not invent a sign-off.

1.3 Capacity guardrail

P0 is the decision loop. This matrix fully specifies the four shipped P0 screens. P1 screens (executive, match, feedback, insights, standalone actions) are mapped at class level so T3.9 / T5.6 are not blocked — they are not a nine-screen permission redesign.


2. Roles

Closed set for MVP. Tags match ADR 0001. One person may hold one primary role.

Role tag Who Job in Orbit P0 home
pm Product Funnel friction, retention peak, product themes /journey, /retention, /feedback
ops CX / ops At-risk queue, assign/close, payment-friction follow-up /at-risk (+ 360 for the client under action)
support Support Orient on one client in < 1 minute; no analytics suite /clients/[clientId]
matching Matching team Therapist fit (P1 T3.5 /match-quality); P0: 360 therapist + journey therapist filter /match-quality
leadership Leadership “Are we healthier this week?” (P1 T3.1); P0: retention + at-risk counts P1 executive; P0 /retention
admin Orbit admin (as needed) Invite/disable (T5.5); matrix edits (T5.6); not a decision-loop analyst Settings (Phase 5)

admin is not a substitute for ops. It does not grant raw gateway payloads or clinical notes.

No consumer, therapist-as-user, or finance-only role in MVP. If a finance specialist needs row-level amounts, assign ops (or a future finance tag in T5.5 — out of this lock).


3. PII / privacy classes (labeled)

Property-level classes are the T1.12 closed set, plus restricted from DATA-MODEL / T1.3 for feedback that is not in the 12-event schema pack. Do not invent a seventh class.

Class Meaning T1.12 / mapping examples PII?
operational Business metadata safe for general internal analytics session_number, booking_context, canonical_type, risk_level, funnel counts No
identifier Direct client (or device) identifiers user_id, anonymous_id, device_id, Client 360 clientId / display label Yes (direct)
financial Payment amounts and gateway metadata amount, currency, gateway on payment_* Sensitive commercial; not a card secret
internal Staff/ops identifiers and case workflow owner_id, resolution_code, cancel_actor, action owner Staff PII, not client clinical
scheduling Slot/time display (UTC store; Asia/Tehran UI) scheduled_start_utc, user_timezone, expected_return_date Indirect (can fingerprint with identifier)
restricted Clinical-adjacent scores and free text FDB scores / ORS/SRS, free_text (DATA-MODEL.md feedback_response) Yes (sensitive)

Not a privacy class — never stored or shown in Orbit UI:

Forbidden Why
Therapy notes, recordings, session content Architecture §12 / SDK ingestion §12
PAN, CVV, IBAN, cardholder name, gateway tokens, write keys, raw gateway callbacks Payment secrets (§5)
Collector write_key secret (write_key_id only) T0.4 / T2.2

Event-level T1.12 labels (payment_succeeded = financial) describe the payload, not the timeline row. A timeline row that shows only canonical_type + time + entity id is operational + identifier, even when the underlying event is financial.


4. Access dimensions (T5.6)

SET-03 / T5.6 dimensions. This matrix fills them. Enforcement is T5.6.

Dimension Meaning
view API may return the field; UI may render it
export Field may appear in CSV / download / scheduled report
admin May change workspace, users, or this matrix (T5.5 / T5.6)
sensitive case May open restricted case notes / ORS-SRS raw / free-text (MVP: no P0 role except a future explicit grant; default deny)

UI hiding without API deny is not access. T5.6 must enforce on the domain API.

Legend used in tables below:

Code Meaning
V view allow
E export allow (implies view of the exported grain)
M view masked — type/status/count only; values stripped
A aggregate only (no row-level identifier)
— deny view and export
S sensitive-case grant (not assigned to P0 roles)

5. Payment secrets policy (locked)

Test bar: Support can see the Client 360 timeline without unnecessary payment secrets.

5.1 Policy (normative)

  1. Support may view the event timeline for a client they are allowed to open. Timeline is the P0 “what happened?” job (SCREENS-SPEC.md #4; T2.10).
  2. Timeline must not include payment secrets or financial values. A payment_succeeded / payment_failed row is allowed as: occurred_at, canonical_type, entity_type, entity_id (payment/order id). Not allowed on the timeline: amount, currency, gateway, refund breakdown, or any secret in §5.2.
  3. Operational payment status is minimum-necessary for support (PRD C360-05): succeeded / failed / duplicate-or-missing, plus order_id / payment_id. That is enough to interpret a stuck booking or return without amounts.
  4. Row-level financial (amount, currency, gateway) is ops-only on Client 360. PM, matching, leadership, and support: deny (or mask to status-only).
  5. Payment secrets are deny for every role, including admin. They must not be in canonical properties, quarantine messages, fixtures, or exports. Collector already forbids write-key storage.

5.2 What counts as a payment secret vs `financial`

Kind Examples Support timeline Support 360 payments panel Ops 360 Any export
Secret (never) PAN, CVV, IBAN, cardholder name, gateway token / authority secret, webhook secret, write key, raw callback body, refund destination account — — — —
financial (gated) amount, currency, gateway (zarinpal / idpay / manual) — (strip from timeline) — (status only) V M except ops+admin audited export
Operational payment canonical_type payment_*, status succeeded/failed, order_id / payment_id, failure_code V V V M (ids hashed or omitted in general export)

5.3 As-built vs policy (honest)

Surface As-built (T2.10) Policy
Timeline occurred_at + canonical_type + entity id — no amount/gateway (apps/web/src/app/clients/[clientId]/page.tsx timeline <ol>) Matches. Support can see timeline without payment secrets.
Payments panel Columns Payment / Status / When / Amount Gap. Amount is financial; support must not see it. T5.6 strips amount/currency/gateway from the 360 API for support (and pm / matching / leadership).
Auth Any holder of INTERNAL_STAFF_TOKEN sees the same 360 payload Gap. Role tags are T5.5; field deny is T5.6.

Do not “fix” the payments-panel amount in this task. Document the gap; T5.6 closes it.


6. Support 360 — minimum necessary

Support’s job: open one client and understand context in < 1 minute. Not: analytics, bulk export, raw events, clinical scores, payment amounts.

6.1 Allowlist (view)

360 block (T1.8 / T2.10) Classes Support
At a glance: clientId + display label, current_stage, therapist id/label, session count, last session, gap, pause/completion/next-booked, risk_v1 level + reasons identifier + operational + scheduling V — required to orient
Sessions table: session number, status, completed time, therapist id, gap operational + scheduling + therapist identifier V
Event timeline: time, canonical_type, entity id operational + identifier (entity id) V — no payload dump, no financial properties, no secrets
Payments panel: payment id, status, when operational + identifier V
Payments panel: amount, currency, gateway financial —
Feedback: form type + submitted time + “present/missing” operational V (empty is OK; T2.7 not in slice)
Feedback: ORS/SRS scores, free_text restricted — (C360-06: general role gets summary/flag only; MVP flag = missing vs present)
Actions: title, source, owner, status, result internal + operational V (read). Assign/close primary path is /at-risk (ops). Support may view; may not reassign unless T5.6 later grants it.
Raw event / payload / quarantine body mixed — (C360-03: raw event only for authorized role; not support)
Clinical notes / session content forbidden —

6.2 Deny list (support)

  • financial values (amount, currency, gateway)
  • Payment secrets (§5.2)
  • restricted scores and free text
  • anonymous_id / device_id (keep clientId only)
  • Bulk client list export, funnel CSV, or “download 360”
  • /dq evidence snippets beyond already-masked operational IDs (T5.2 already masks PII at write)
  • Admin / matrix / invite

6.3 Other roles on 360

Role 360 view Why
ops Full support allowlist plus financial on the payments panel (not secrets) Payment is the top friction stage (T1.1); C360-05 authorized finance for follow-up
pm / matching / leadership Same as support (no financial, no restricted) when they drill from at-risk / journey Need path context, not amounts or ORS
admin Same as ops for 360 data; plus audit of access (T5.5). Still no secrets, no clinical notes

7. Screen access (role × screen)

P0 routes are shipped. P1 routes are mapped (T1.8 §8) — grant is for when built.

Screen Route Task pm ops support matching leadership admin
Client 360 /clients/[clientId] T2.10 V V V (primary) V V V
Client journey /journey T3.2 V (primary) V — V V V
Retention & churn /retention T3.3 V (primary) V — V V V
At-risk + actions /at-risk T3.4+T3.8 V V (primary) V (read; assign is ops) V V V
Executive summary /summary T3.1 V V — — V (primary) V
Match quality /match-quality T3.5 V V — V (primary) V V
Feedback (aggregate) /feedback T3.6 V V — V V V
Insights /insights T3.7 V V — V V V
Actions (standalone) P1 — V V V — V V
Role saved views /reports T3.9 per role shortcut per role per role per role per role V
DQ dashboard / issues /dq T2.11, T5.1–T5.2 — V — — — V
Integrations /integrations T5.3 — — — — — V
Settings / matrix Phase 5 T5.5–T5.6 — — — — — V

Support does not need journey/retention to do 360. Deny keeps the role on minimum necessary. Ops does need 360 for the client on the at-risk row.

As-built gap: /journey is ungated; /clients, /retention, /at-risk share one staff token (T1.8 §3). T5.5/T5.6 apply this table.


8. Role × screen × data-class (P0)

Grain: what the screen may render for that role. Aggregate screens must not leak row-level identifier / financial / restricted in the default view.

8.1 Client 360 (`/clients/[id]`, T2.10)

Class pm ops support matching leadership
operational V V V V V
identifier (client/therapist ids, labels) V V V V V
scheduling V V V V V
internal (actions) V V V V V
financial — V — — —
restricted — — — — —
Payment secrets — — — — —
Export — — — — —

Support timeline row = operational + entity identifier only (§5). That is the Test Agent check.

8.2 Client journey (`/journey`, T3.2)

Class pm ops support matching leadership
operational (stage counts, conversion, drop-off, median time) A+E A — (no screen) A A
identifier — — — — —
financial A (paid-stage count only, not amounts) A — A A
restricted — — — — —
scheduling (median time between stages) A A — A A
Export E aggregate E aggregate — — E aggregate

Drill-down from a stage to a client list is not P0 (PRD RPT-06). When built: requires view on 360 identifier and is not a general export.

8.3 Retention & churn (`/retention`, T3.3)

Class pm ops support matching leadership
operational (continue N→N+1, churn-by-session, gap indicators) A+E A — A A+E
identifier — — — — —
financial — — — — —
restricted — — — — —
Export E aggregate E aggregate — — E aggregate

8.4 At-risk + actions (`/at-risk`, T3.4 + T3.8)

Class pm ops support matching leadership
operational (risk level, reasons, suggested step, gap days) V V V V V
identifier (client id/label on the row) V V V V V
scheduling (last session time) V V V V V
internal (owner, action status, outcome) V V (assign/close) V (read) V (read) V (read)
financial — — — — —
restricted — — — — —
Export — E list without financial/restricted; ids allowed for ops — — —

risk_v1 reasons are operational copy (no clinical claims). They may mention payment failure as a reason code, not an amount.


9. Export rules (locked)

Rule Policy
Default Export is opt-in per role × grain. If a cell is not E, export is deny.
Aggregate vs row Journey and retention exports are counts and rates only — no user_id, email, phone, amount.
Client-level No 360 “download client” in P0. Ops at-risk list export may include client_id + operational risk fields; mask financial; omit restricted.
Financial in export Public / general / PM / leadership / support: mask (C360-05, RPT-06). Ops/admin row export of amounts: only via T5.6 grant + audit (T5.5). Until T5.6, treat as deny.
Restricted Never in general export. Never for support. Sensitive-case dimension only (unassigned in P0).
Identifiers in export Hash or omit in any file that leaves the staff UI (email to self, CSV). Ops queue CSV may keep client_id as an operational key.
Attribution / insight labels If an export includes insights later (P1), keep causal-strength badges (PRD OUT-04) — not a P0 path.
Scheduled exports P1 reports (PRD). Same class rules as interactive export.
Fixtures / reports in git Synthetic IDs only (usr_*, thr_*). No real PII. Unchanged.

Capability 15 shortcuts (SCREENS-SPEC) are saved views, not extra data grants. Shipped (T3.9) at /reports. Ops / PM / support first. Shortcuts do not expand class access.

Role Saved view (T3.9) Filtered href Priority Export
Ops at-risk + actions /at-risk?level=high_risk&sort=gap P0-critical destination Row operational + client_id; no finance/restricted
PM journey + feedback product themes (theme=tech) /reports?view=pm-journey-feedback P1 Aggregate only
Support client 360 + sensitive flags (not raw ORS) /reports?view=support-360-flags P0-critical destination No export
Matching fit quality — /match-quality?therapist=thr_a /match-quality?therapist=thr_a P1 Aggregate
Leadership T4.7 KPI pack + action completion /reports?view=leadership-kpi P1 Aggregate
Leadership Executive summary (T3.1) /summary P1 Aggregate

10. P1 / DQ / admin (mapped, not redesigned)

Surface Data classes on screen Who Note
Executive (T3.1) operational aggregates leadership, pm No identifier
Match quality (T3.5) operational per therapist id matching primary Therapist id is provider identifier, not client PII
Feedback aggregate (T3.6) operational themes; restricted denied in default pm, matching, ops Raw ORS/SRS = sensitive-case only
Insights (T3.7) operational all except support (optional later)
/dq operational counters + masked snippets (T5.2) ops, admin Page copy: no PII
Settings (T5.5–T5.6) internal staff records admin Audit invite/revoke/matrix edits

11. T5.6 field catalog (implementation input)

Backend must deny by field, not by hiding a card. Minimum fields to gate:

Field / path Class support ops pm / matching / leadership
client.client_id, display label identifier V V V
anonymous_id, device_id, email, phone identifier — — —
current_stage, session_count, gaps, pause/completion, risk_* operational V V V
Timeline canonical_type, occurred_at, entity_id operational + identifier V V V
Timeline / API properties.amount (and full payload) financial / mixed — V on payments panel only; — on timeline —
payment.amount, currency, gateway financial — V —
payment.status, payment_id, order_id, failure_code operational V V V
feedback.scores, free_text restricted — — —
feedback.form_type, submitted_at operational V V V
action.* (no extra client PII) internal V V + write assign/close V
Gateway secrets / PAN / write keys secret — — —

Negative access (T5.6 test-as-role): support GET /api/clients/:id must omit payments[].amount (and currency/gateway) even if the UI is patched.


12. Out of scope / residuals

Item Owner
Browser consent, anonymous ID cookies, what the browser must not send T1.14 — BROWSER-CONSENT-POLICY.md. Implementation T2.13. Named security/product sign-off residual (T4.10).
Auth.js users, invite/disable, role assignment T5.5 (not done)
Backend enforce + matrix admin UI + test-as-role T5.6 (blocked by this task + T5.5)
Signed security checklist, audit logging bar, residency T4.10 checklist SECURITY-REVIEW-T4.10.md. Named human sign-off residual. Per-user access logs owned to T5.5. Residency still parked (ADR 0001 / T4.1).
Strip amount from as-built 360 payments panel T5.6 (documented gap §5.3)
Named security/product approval of this matrix T4.10 / Approve Agent — do not invent
SSO / IdP Parked in ADR 0001; optional T5.5
Finance-only role Not in MVP role set
Clinical notes in Orbit Forever out of scope

13. References