--- status: template playbook-section: 10 title: "Data Subject Rights (DSR) Fulfilment Procedure" gdpr-articles: [Art. 15, Art. 16, Art. 17, Art. 18, Art. 20] last-reviewed: "[FILL IN: YYYY-MM-DD]" --- # Data Subject Rights (DSR) Fulfilment Procedure > **Template status** — fill every `[FILL IN: …]` marker before use. > Cross-references below point to shipped interfaces, endpoints, and ADRs; do not change them. --- ## 1. Purpose & Scope This procedure governs how `[FILL IN: organisation name]` receives, validates, fulfils, and records requests from data subjects exercising rights under GDPR Articles 15–20. **Covered rights:** | GDPR Article | Right | Shipped interface | | ------------ | --------------------------------- | ----------------------------------------- | | Art. 15 + 20 | Access + Portability | `IDataExport` (`dsr.export`) | | Art. 16 | Rectification | `IDataRectify` (`dsr.rectify`) | | Art. 17 | Erasure ("right to be forgotten") | `IDataDelete` (`dsr.delete`) | | Art. 18 | Restriction of processing | `IProcessingRestriction` (`dsr.restrict`) | See [`docs/guides/dsr.md`](../../guides/dsr.md) for the engineering cookbook and [`docs/decisions/adr-025-eu-compliance-baseline.md`](../../decisions/adr-025-eu-compliance-baseline.md) § Epic B for the design rationale. --- ## 2. Roles & Contacts | Role | Name | Contact | | -------------------------------- | ------------------------------ | ------------------ | | Data Protection Officer | [FILL IN: name / DPO provider] | [FILL IN: contact] | | DSR Coordinator | [FILL IN: name] | [FILL IN: contact] | | Engineering contact (fulfilment) | [FILL IN: name] | [FILL IN: contact] | **Intake channel:** `[FILL IN: e.g. privacy@example.com / online form URL]` --- ## 3. Statutory Deadlines | Stage | Deadline | Extension | | ------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Acknowledge receipt | `[FILL IN: e.g. 5 business days]` | — | | Fulfil or refuse | **1 calendar month** from receipt (GDPR Art. 12(3)) | Up to 2 further months for complex/numerous requests; notify subject within the first month | | Notify subject of refusal | 1 month | — | Deadline clock starts when the organisation receives the request — not when identity is verified. --- ## 4. Phase 1 — Receipt & Intake ### 4.1 Intake channels Accept DSR requests via: - `[FILL IN: primary channel, e.g. privacy form at https://example.com/privacy/request]` - `[FILL IN: secondary channel, e.g. email to privacy@example.com]` - Any written format (GDPR does not mandate a specific form) ### 4.2 Logging the request Log each incoming request immediately in `[FILL IN: DSR register / issue tracker]` with: | Field | Value | | --------------- | --------------------------- | | Reference ID | `DSR-YYYY-NNN` (sequential) | | Right requested | Art. 15 / 16 / 17 / 18 / 20 | | Channel | email / form / letter | | Received at | ISO 8601 timestamp | | Deadline | received + 1 month | | Status | `pending-verification` | ### 4.3 Acknowledgement Send acknowledgement to the requester within `[FILL IN: e.g. 5 business days]` confirming: - Receipt of the request - Reference ID - Identity verification requirement (§ 5) - Statutory deadline Template: `[FILL IN: path to acknowledgement email template]` --- ## 5. Phase 2 — Identity Validation ### 5.1 Verification requirement GDPR Art. 12(6) permits identity verification when there is reasonable doubt. Always verify for Art. 17 (erasure) and Art. 18 (restriction). For Art. 15 (access) of clearly authenticated users, verification may be satisfied by existing session. ### 5.2 Verification methods | Method | Acceptable for | | ---------------------------------------------- | ---------------------------------- | | Authenticated session in the app | Art. 15, Art. 16 (low-risk fields) | | Email confirmation to registered address | All rights | | `[FILL IN: government ID / identity provider]` | Art. 17 cascade-hard, Art. 18 | ### 5.3 Rejected or suspicious requests If identity cannot be verified after `[FILL IN: e.g. 14 days]`: 1. Close the request with status `identity-unverified`. 2. Notify the requester in writing of the outcome. 3. Log closure in the DSR register. --- ## 6. Phase 3 — Fulfilment ### 6.1 Identify the subject record Map the verified identity to the system `subjectId` (Payload `users.id` or equivalent auth collection primary key). Cross-reference `compliance/data-map.yml` (generated by `pnpm compliance:data-map`) to enumerate which collections hold personal data for this subject. ```bash # Regenerate data map to ensure it reflects current schema pnpm compliance:data-map ``` Review `compliance/data-map.yml` fields tagged `exportable: true` (Art. 15/20) and `restrictable: true` (Art. 18). ### 6.2 Art. 15 + 20 — Access and Portability **Interface:** `IDataExport.exportSubjectData(subjectId, format)` **tRPC procedure:** `dsr.export` (query — no mutation) **GDPR deadline:** respond within 1 month; provide data without charge for first copy. Invoke via the admin tRPC client or the REST endpoint wired in `apps/web-next`: ``` GET /api/gdpr/export?subjectId=&format=json GET /api/gdpr/export?subjectId=&format=json-ld # machine-readable JSON-LD ``` The response is a `UserDataBundle`: - `data` — keyed by Payload collection slug; each bucket contains `asSelf` (rows the subject owns) and `asReference` (rows merely referencing the subject) - `auditLog` — the subject's audit trail (optional; include for transparency) - `exportedAt` — ISO 8601 export timestamp for the certificate of fulfilment **Audit entry emitted:** `EXPORT` action, `resource.type: "data-subject"`, `resource.id: subjectId`. Deliver the export to the subject via `[FILL IN: secure delivery method, e.g. encrypted download link / secure email]`. ### 6.3 Art. 16 — Rectification **Interface:** `IDataRectify.updateSubjectField(subjectId, collection, field, value)` **tRPC procedure:** `dsr.rectify` (mutation) **Scope:** only fields tagged `custom.pii` in the Payload collection config (i.e. fields indexed in `compliance/data-map.yml`). ``` POST /api/gdpr/rectify Body: { subjectId, collection, field, value } ``` Steps: 1. Confirm the field is `custom.pii`-tagged in `compliance/data-map.yml`. 2. Confirm the new value passes Payload field validation. 3. Invoke the procedure; the implementation emits a `RESTRICT` audit entry with `reason: "art-16-request"`. 4. Notify the subject that rectification is complete and, if data was shared with third parties, notify them per Art. 19. Third-party notification log: `[FILL IN: log location]` ### 6.4 Art. 17 — Erasure **Interface:** `IDataDelete.deleteSubjectData(subjectId, mode)` **tRPC procedure:** `dsr.delete` (mutation — requires authenticated admin session for `cascade-hard`) **Returned:** `DeletionCertificate` — retain this as evidence of fulfilment. **Mode selection:** | Mode | Effect | Who can invoke | | ---------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | | `"soft"` | Redacts PII fields in owned rows; NULLs reference fields; row structure preserved | Standard authenticated user or DSR coordinator | | `"cascade-hard"` | Hard-deletes owned rows where `postDeletion.action === "hard-delete"` per retention policy; NULLs reference fields | Admin role required | ``` POST /api/gdpr/delete Body: { subjectId, mode: "soft" | "cascade-hard" } ``` **Exemptions** (GDPR Art. 17(3)) — erasure must be refused if data is required for: - Legal obligation compliance (document refusal in the DSR register) - Establishment, exercise, or defence of legal claims **Audit trail pseudonymization:** after deletion, invoke `IAuditLog.eraseSubject(actorId, "pseudonymize")` via the admin tRPC to replace the subject identifier in the audit log with `erased-{hash[0:16]}` (sha256-salted pseudonym per ADR-018). This preserves the event record for regulatory purposes while removing the identifier. **Retention-policy overrides:** check `compliance/retention-policy.yml` for `postDeletion` rules — some collections may have a grace period (`postDeletion.duration: P30D`) before hard deletion executes. ```bash # View current retention policy cat compliance/retention-policy.yml # Regenerate from Payload field tags pnpm compliance:retention-policy ``` **DeletionCertificate fields to retain:** - `subjectId` (or pseudonymized form) - `mode`, `timestamp`, `reason: "art-17-request"` - `affected[]` — collections and rows modified - `auditEntryId` — links to the audit log ### 6.5 Art. 18 — Restriction of Processing **Interface:** `IProcessingRestriction.setRestriction(subjectId, granted: true)` **tRPC procedure:** `dsr.restrict` (mutation) ``` POST /api/gdpr/restrict Body: { subjectId, granted: true } ``` The implementation sets `processingRestrictedAt` on the user record. Downstream use cases check `isRestricted(subjectId)` before processing personal data. The audit channel records a `RESTRICT` audit entry on every state change. **Grounds for restriction (Art. 18(1)):** - Accuracy of data is contested (pending Art. 16 rectification) - Processing is unlawful but erasure is opposed by the subject - Organisation no longer needs the data but subject requires it for legal claims - Subject has objected (pending verification of legitimate grounds) Notify the subject when restriction is lifted: invoke `dsr.restrict` with `granted: false` and send written notice per Art. 18(3). **Audit entry emitted:** `RESTRICT` (on restriction) / `UNRESTRICT` (on lift). --- ## 7. Phase 4 — Recording & Closure ### 7.1 DSR register update Update the DSR register entry (§ 4.2) with: | Field | Value | | -------------- | ------------------------------------------------- | | Fulfilled at | ISO 8601 timestamp | | Outcome | `fulfilled` / `refused` / `partially-fulfilled` | | Evidence | DeletionCertificate ID or export reference | | Audit entry ID | From `AuditEntry.correlationId` or `auditEntryId` | | Status | `closed` | ### 7.2 Subject notification Notify the data subject of the outcome within the Art. 12(3) deadline: - Fulfilment: summary of actions taken, delivery method for exports. - Refusal: grounds for refusal (Art. 12(4)), right to lodge complaint with SA, right to seek judicial remedy. Template: `[FILL IN: path to outcome notification template]` ### 7.3 Third-party notification log (Art. 19) If the corrected, erased, or restricted data was previously shared with third parties (sub-processors listed in `compliance/sub-processors.yml`), notify each processor of the subject's request. Record each notification in `[FILL IN: third-party notification log]`. --- ## 8. Consent-Related DSR Interactions Consent grant and withdrawal are distinct from Art. 17 erasure but often accompany DSR requests. The consent channel ([`docs/guides/consent.md`](../../guides/consent.md)) records: | Audit action | Trigger | | ------------------ | -------------------------------------------------------- | | `CONSENT_GRANT` | Subject grants consent for a category (e.g. `marketing`) | | `CONSENT_WITHDRAW` | Subject withdraws consent | | `RESTRICT` | Art. 18 restriction set | | `UNRESTRICT` | Art. 18 restriction lifted | If a DSR request also includes consent withdrawal, invoke `IConsent.withdraw(subjectId, categories)` in addition to the relevant DSR procedure. Consent withdrawal does not automatically trigger erasure — only an explicit Art. 17 request does. --- ## 9. Appendix — Command Reference ```bash # Regenerate PII data map (enumerate affected collections and fields) pnpm compliance:data-map # Regenerate retention policy (check postDeletion rules before Art. 17) pnpm compliance:retention-policy # Regenerate sub-processor list (for Art. 19 third-party notification) pnpm compliance:sub-processors # Regenerate all compliance artifacts in one pass pnpm compliance:emit-all # Run fallow audit before closing a DSR-related PR pnpm fallow:audit ``` --- ## 10. Document Control | Field | Value | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Owner | [FILL IN: DPO or DSR Coordinator] | | Review cycle | [FILL IN: e.g. annual or on schema change] | | Next review date | [FILL IN: YYYY-MM-DD] | | Cross-references | [`docs/decisions/adr-025-eu-compliance-baseline.md`](../../decisions/adr-025-eu-compliance-baseline.md), [`docs/decisions/adr-018-audit-and-compliance.md`](../../decisions/adr-018-audit-and-compliance.md), [`docs/guides/dsr.md`](../../guides/dsr.md), [`docs/guides/audit-and-compliance.md`](../../guides/audit-and-compliance.md), [`docs/guides/consent.md`](../../guides/consent.md), [`../data-map.example.yml`](../data-map.example.yml) |