Data Subject Rights (DSR) Endpoint Specification
Effective Date: May 20, 2026
Version: 1.0
Owner: Identity Engineering + DPO — privacy@vereid.com.
Implemented in: VEREID_API — app/api/v1/me/dsr.py (to be created in Workstream B).
Statutory backdrop: GDPR Art. 15 (access), Art. 16 (rectification), Art. 17 (erasure), Art. 20 (portability); UK GDPR equivalents; CCPA/CPRA §§ 1798.100, 1798.105, 1798.106; AU Privacy Act 1988 APP 12 (access), APP 13 (correction); BIPA biometric revocation.
This document is the engineering contract. The Workstream B agent implements these endpoints exactly to spec; the Workstream A counsel review signs off the surface and behavioural commitments before live PII goes through them.
1. Common contract
1.1 Base path
https://api.vereid.com/v1/me/...
1.2 Authentication
- All DSR endpoints require an authenticated session AND a fresh MFA challenge ≤ 5 minutes old.
- For high-risk requests (deletion of a verified-tier account, export including biometric template), a re-verification through the VEREID ID flow may be required. The decision logic is encapsulated in
dsr_risk_evaluator.py(Workstream B).
1.3 Rate limit
1 request per minute per endpoint per subject(slowapi,headers_enabled=False).- Burst-clamp at 3 / hour to prevent denial-of-service via repeated 30-day soft-delete cycles.
1.4 Response envelope
All endpoints return:
{
"request_id": "uuid",
"subject_id_hash": "hex",
"status": "queued|in_progress|complete|failed",
"received_at": "RFC3339",
"expected_completion_at": "RFC3339",
"links": {
"status": "https://api.vereid.com/v1/me/dsr/{request_id}",
"result": null
}
}
1.5 Audit
Every request writes to dsr_audit (append-only) with:
request_id,subject_id_hash,endpoint,requested_at,executor(self|legal|support),evidence_bundle_uri,outcome,outcome_at,legal_basis_for_any_redaction.
1.6 Notification
On status change, an email is sent (SendGrid) with the request's anti-phishing code and a deep link.
1.7 Standard error codes
401 unauthenticated403 mfa_required403 reverification_required409 prior_request_in_progress423 legal_hold_active429 rate_limited451 retention_blocked_by_law(with areasonenum:aml,tax,litigation,regulator)
2. POST /v1/me/data-export
2.1 Purpose
Return all personal data held about the requester in a structured, commonly used, machine-readable format (JSON). Async (some users have GBs of media).
2.2 Request
POST /v1/me/data-export
{
"scope": ["account", "social", "verification", "audit", "billing", "all"],
"format": "json",
"include_media": true,
"tenant_id": null
}
scope— at least one of the above."all"is shorthand.format—jsononly at v1.csvplanned.pdfsummary planned.include_media— iftrue, media files are bundled as references with signed URLs; iffalse, only metadata.tenant_id— for VEREID Auth / VEREID ID end-users, restrict to a single tenant (required where the subject's data exists across multiple Customers).
2.3 Response (sync acknowledgement)
HTTP/1.1 202 Accepted
{
"request_id": "uuid",
"subject_id_hash": "hex",
"status": "queued",
"received_at": "2026-05-20T05:00:00Z",
"expected_completion_at": "2026-05-21T05:00:00Z",
"scope": ["all"],
"links": {
"status": "https://api.vereid.com/v1/me/dsr/{request_id}",
"result": null
}
}
2.4 Result (when status = complete)
{
"request_id": "uuid",
"status": "complete",
"result": {
"manifest_url": "https://s3-presigned-...",
"manifest_sha256": "hex",
"expires_at": "2026-05-28T05:00:00Z",
"size_bytes": 12345678,
"tenant_breakdown": [
{
"tenant_id": "tenant_abc",
"size_bytes": 9876543,
"categories": ["account", "verification"]
}
]
}
}
Bundle layout inside the manifest:
/manifest.json
/README.txt (plain-English explanation)
/account.json
/social/posts.jsonl
/social/comments.jsonl
/social/dms.jsonl
/social/follows.jsonl
/verification/jobs.jsonl
/verification/document-images/ (signed URLs in manifest, only if include_media)
/billing/invoices.jsonl
/audit/events.jsonl
/consent/events.jsonl
2.5 Behavioural commitments
- Biometric face templates are exported as their vector representation only, with a header explaining they are not re-identifiable images. Raw selfie/liveness frames may also be exported within the 30-day window; after purge they are unrecoverable and the manifest will say so.
- Document images are exported with the same KMS-DEK-shred caveat.
- Audit events that contain other subjects' PII (e.g. a moderator-action row that lists a reporter) are redacted to the minimum needed to explain the action.
- Bundle is encrypted client-side with a passphrase chosen at request time (or with a one-time recovery key emailed separately) before upload to S3.
- Signed URL expires in 7 days; bundle deleted from S3 at 30 days.
2.6 SLA
- Free / Social user: complete within 30 days.
- Paid Customer staff: complete within 7 days.
- B2B-routed end-user request (initiated by the Customer): complete within 5 business days.
3. POST /v1/me/delete
3.1 Purpose
Delete the subject's account and associated personal data, subject to lawful retention. Two-phase: 30-day soft-delete window → irreversible hard-delete cascade.
3.2 Request
POST /v1/me/delete
{
"confirm": "DELETE-{handle}",
"reason": "no_longer_using|privacy_concern|moved_provider|other",
"reason_other": null,
"delete_media": true,
"tenant_id": null
}
confirm— must literally equal the stringDELETE-{handle}(lowercased handle) to prevent accidental deletion.tenant_id— required for VEREID Auth / VEREID ID end-users with multi-tenant footprint; the subject must issue one request per tenant.
3.3 Response
HTTP/1.1 202 Accepted
{
"request_id": "uuid",
"status": "soft_deleted",
"hard_delete_at": "2026-06-19T05:00:00Z",
"undo_url": "https://app.vereid.com/account/restore/{single-use-token}",
"blocked_categories": [
{
"category": "verification.document_image",
"reason": "retention_blocked_by_law",
"code": "aml",
"release_at": "2033-05-20T05:00:00Z"
}
]
}
3.4 Behaviour
- Immediately:
- Account marked
state=soft_deleted. - Login refused.
- Public profile + posts + comments hidden from feed.
- Active sessions revoked.
- In-flight verification jobs aborted; their biometric artefacts shredded immediately (faster than the 30-day default).
- DMs removed from recipients' inboxes (replaced with "Account deleted" placeholder).
- Account marked
- During 30-day window:
- User can restore via the
undo_url(one-time token, single use). - Customer-staff visibility into the deleted account is restricted to legal/compliance roles.
- User can restore via the
- At hard-delete:
- Personal data is irreversibly removed across all stores (Aurora row deletes + S3 object deletes + KMS DEK shreds).
- Anonymised foreign-key stub retained where referential integrity requires (
subject_id_hashonly). consent_eventsretained (legal-defence).- Document images retained for the AML-mandated 7 years; release date returned in
blocked_categories. - Hard-delete completion event posted to EventBridge; audit row written.
3.5 Excluded from delete
- Audit events (kept for 7 years).
- Document images and OCR fields (kept for 7 years, AML).
- Billing records (kept for 7 years, tax law).
- Court-order or regulator legal-hold artefacts.
3.6 Notification
- Confirmation email on soft-delete with the
undo_url. - Confirmation email on hard-delete with a final summary of what was deleted and what was retained-by-law (and when each retained item will be deleted).
4. POST /v1/me/rectify
4.1 Purpose
Correct inaccurate personal data — name, DOB, address, contact, profile fields.
4.2 Request
POST /v1/me/rectify
{
"field": "legal_name|date_of_birth|residential_address|email|phone|display_name|handle|bio|jobs.title|jobs.employer",
"current_value_hash": "sha256_of_current_value",
"new_value": "Mina Tadros",
"evidence": {
"type": "document|self_attest|government_record_lookup",
"verification_job_id": "ver_abc123",
"notes": "Updated passport"
}
}
current_value_hash— server-computed hash of current value; protects against optimistic-concurrency conflicts and against a stale UI overwriting a recent change.evidence— required for any identity-bearing field (legal_name,date_of_birth,residential_address). Fordisplay_name,handle,bio,jobs.*evidence is optional.
4.3 Response
HTTP/1.1 200 OK
{
"request_id": "uuid",
"status": "complete",
"field": "legal_name",
"previous_value_redacted": "M*** T*****",
"new_value_redacted": "M*** T*****",
"evidence_used": "verification_job:ver_abc123",
"applied_at": "2026-05-20T05:00:00Z",
"audit_event_id": "evt_xyz"
}
4.4 Behaviour
- Each successful rectification writes both an
audit_eventand an entry in the field-history table so that prior values are retained for legal-defence (encrypted, restricted IAM). - Where the corrected field affects downstream Customer state (e.g. an updated legal name pushed to a B2B Customer's tenant), VEREID notifies the Customer via webhook
user.profile.rectified. - Where the rectification contradicts the most recent verification, the verification is marked
staleand the subject is prompted to re-verify. - We will not "rectify" by deleting; rectification preserves the historical row in an encrypted append-only audit table for the lifetime of the account + 7 years.
4.5 Refusal grounds
- Field cannot be self-attested (e.g. you cannot change your
date_of_birthto a value younger than 16). - Rectification would create a sanctions-list collision (we'll suspend pending compliance review).
- Suspected impersonation — re-verification required.
5. Related endpoints (for completeness)
These are not the three primary DSR endpoints, but are exposed to satisfy specific statutory rights:
| Endpoint | Right | Notes |
|---|---|---|
POST /v1/me/biometric-revoke | BIPA-style consent withdrawal | Triggers 7-day biometric shred (faster than default 30d). |
POST /v1/me/restrict | GDPR Art. 18 | Suspends processing while a dispute is investigated. |
POST /v1/me/object | GDPR Art. 21 | For legitimate-interest or direct-marketing processing. |
GET /v1/me/processing | Transparency | Returns the live RoPA scoped to the requester. |
POST /v1/me/consent | Consent grant/withdraw | Updates consent_events and recomputes downstream policy. |
POST /v1/me/dsr/{id}/cancel | Cancel a queued request | Allowed for data-export only. |
6. Webhooks (B2B Customers)
Where the subject's data exists in a Customer's tenant, the Customer receives:
| Event | Payload | When |
|---|---|---|
subject.data_export_requested | request_id, subject_id_hash, tenant_id | At request |
subject.deletion_requested | + hard_delete_at | At soft-delete |
subject.deletion_completed | + completed_at | At hard-delete |
subject.profile_rectified | field, applied_at | At rectification |
All webhooks are HMAC-signed (SHA-256, hmac_auth.py).
7. Observability & SLOs
- Dashboard
dsr-slostracks p50/p95 fulfilment time per endpoint per plan tier. - Alert if any open request exceeds 90% of SLA.
- Alert if any soft-delete is missed at hard-delete time.
- Alert if a rectification rate spike correlates with an account-takeover wave.
8. Change history
| Date | Version | Change | Approver |
|---|---|---|---|
| 2026-05-20 | 1.0 | Initial spec | DPO + Identity Eng |
[TBD: counsel review — verify the 7-day biometric-shred SLA satisfies BIPA "as soon as possible"; verify the 30-day soft-delete is compatible with CCPA "without undue delay"; verify the redaction approach for audit exports; finalise subject_id_hash peppering.]
