Skip to content
Legal

Data subject request endpoints

How to exercise your data subject rights.

Data Subject Rights (DSR) Endpoint Specification

Effective Date: May 20, 2026 Version: 1.0 Owner: Identity Engineering + DPO — privacy@vereid.com. Implemented in: VEREID_APIapp/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 unauthenticated
  • 403 mfa_required
  • 403 reverification_required
  • 409 prior_request_in_progress
  • 423 legal_hold_active
  • 429 rate_limited
  • 451 retention_blocked_by_law (with a reason enum: 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.
  • formatjson only at v1. csv planned. pdf summary planned.
  • include_media — if true, media files are bundled as references with signed URLs; if false, 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 windowirreversible 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 string DELETE-{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

  1. 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).
  2. 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.
  3. 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_hash only).
    • consent_events retained (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). For display_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_event and 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 stale and 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_birth to a value younger than 16).
  • Rectification would create a sanctions-list collision (we'll suspend pending compliance review).
  • Suspected impersonation — re-verification required.

These are not the three primary DSR endpoints, but are exposed to satisfy specific statutory rights:

EndpointRightNotes
POST /v1/me/biometric-revokeBIPA-style consent withdrawalTriggers 7-day biometric shred (faster than default 30d).
POST /v1/me/restrictGDPR Art. 18Suspends processing while a dispute is investigated.
POST /v1/me/objectGDPR Art. 21For legitimate-interest or direct-marketing processing.
GET /v1/me/processingTransparencyReturns the live RoPA scoped to the requester.
POST /v1/me/consentConsent grant/withdrawUpdates consent_events and recomputes downstream policy.
POST /v1/me/dsr/{id}/cancelCancel a queued requestAllowed for data-export only.

6. Webhooks (B2B Customers)

Where the subject's data exists in a Customer's tenant, the Customer receives:

EventPayloadWhen
subject.data_export_requestedrequest_id, subject_id_hash, tenant_idAt request
subject.deletion_requested+ hard_delete_atAt soft-delete
subject.deletion_completed+ completed_atAt hard-delete
subject.profile_rectifiedfield, applied_atAt rectification

All webhooks are HMAC-signed (SHA-256, hmac_auth.py).


7. Observability & SLOs

  • Dashboard dsr-slos tracks 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

DateVersionChangeApprover
2026-05-201.0Initial specDPO + 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.]