Skip to main content

Data subject requests

A data subject request (DSR) is a person asking what we hold about them, or asking us to correct, export, restrict or delete it. The law gives a deadline. This page shows how to answer every request completely and on time, across every place the data lives.

How it works​

The rights, in plain words​

RightWhat the person can askGDPRDPDP Act
Access"What do you hold about me, and who did you share it with?"Art. 15s.11
Correction"Fix this wrong data"Art. 16s.12
Erasure"Delete my data"Art. 17s.12
Restriction"Stop using it while we sort this out"Art. 18Not a separate right
Portability"Give it to me in a machine-readable file"Art. 20Not a separate right
Objection"Stop processing on legitimate interests or for direct marketing"Art. 21Withdraw consent instead (s.6(4))
Automated decisions"Have a human review this decision"Art. 22Not a separate right
Grievance"I want to complain to you"Complaint to the authoritys.13, must be answered
Nominate"Let this person act for me if I die or cannot act"Nos.14

Deadlines, as of October 2026​

LawDeadlineSource
GDPRWithout undue delay, and within one month of receipt. Can be extended by two further months for complex or many requests; tell the person within the first month, with reasons.GDPR Art. 12(3), EUR-Lex
DPDPRule 14 requires fiduciaries to publish their response period for grievances, not more than 90 days. Rule 14 applies from about 13 May 2027. The Act itself sets no separate deadline for access or erasure: verify before relying on this.DPDP Rules 2025, MeitY
App storesNo legal deadline, but the app must say how long deletion takes.Google Play account deletion

Use the strictest one as the default. A single 30-day target for every request meets both.

How to build it​

1. One request table, one step table​

create table dsr_request (
id uuid primary key,
user_id uuid, -- null if the person has no account
subject_ref_hash text not null, -- hash of email or phone; survives erasure
type text not null, -- access, export, correction, erasure, restriction, objection
scope text not null, -- whole account, or one product
source text not null, -- app, web, staff (from an email)
status text not null, -- pending, in_progress, completed, cancelled, rejected
received_at timestamptz not null,
identity_verified_at timestamptz,
due_at timestamptz not null, -- received_at + 30 days
extended_to timestamptz, -- with a reason, inside the first month
completed_at timestamptz,
handler text
);

create table dsr_step (
request_id uuid references dsr_request(id),
step_key text, -- 'core_account_erase', 'analytics_delete', 'ad_audience_removal'
kind text not null, -- auto (button runs it) or manual (staff ticks it)
status text not null, -- pending, done, failed, skipped, not_applicable
detail jsonb, -- error text, external ticket id
done_by text,
done_at timestamptz,
note text, -- required when skipped
primary key (request_id, step_key)
);

One open request per person and scope. A second tap returns the existing request.

2. Generate the steps from the data map​

Every location in the data map with an erasure action becomes a step. That is how processors, analytics and file versions get onto the list instead of being remembered. Typical steps:

StepKind
Core account erase (profile, sessions, devices)auto
Each product module's sweepauto
Files, every versionauto
AI service's own stores (chat, transcripts, generated audio)auto
Auth provider user recordauto, or manual until built
Analytics user deletion APIauto
Attribution provider deletion requestauto
Ad-audience removal, one per export that contained this personmanual
Sign-in-with-Apple token revokeauto, from the app's delete flow
Backupsnot_applicable, with the documented backup-expiry position

Each auto step calls an idempotent erase for that module and records success or the error. A failed step shows red with a Retry button. Nothing completes until every step is done, skipped with a note, or not applicable.

3. Verify identity, in proportion​

  • A request from inside the signed-in app is already verified.
  • An email request: confirm through the account's own email or phone (a one-time code), not by asking for an ID document.
  • Never reveal whether an account exists to an unverified requester.

4. Account deletion vs tombstone​

Deletion is not one thing. Use clear words in code:

TermMeaning
Soft deleteHidden, recoverable. Not erasure
AnonymisePersonal fields wiped; the row stays so references do not break
Hard deleteThe row or object is gone
TombstoneA minimal record kept after erasure, for example a keyed hash of the email for a short period to stop sign-up abuse

A tombstone is still personal data. Keep it short, keyed (HMAC, not a plain hash), and say so in the privacy policy.

During the deletion window:

  • Lock the account and revoke refresh tokens. Block sign-in with a clear message and the due date.
  • Send "your deletion is scheduled for DATE". Send "your data has been deleted" just before the wipe, because the address is gone after.
  • Let staff cancel with a reason, which unlocks the account.
  • Do not auto-erase when the deadline is missed. Alert the team at 7 and 2 days left.

5. Access and export​

  • Build it as an async job: request, then poll status. Fan out to each module, the same traversal as erasure in reverse.
  • Format: JSON plus media in a ZIP. JSON is structured, commonly used and machine-readable, which is what portability asks for.
  • Store the archive under a per-user prefix with its own 7-day expiry. Give a presigned link that expires at the same time.
  • Rate-limit (for example one per 24 hours), audit every request, and consider re-auth before the request. The export is an exfiltration target.
  • Include the person's consent history.
  • Exclude other people's content and staff-only moderation notes.
  • Do not export data you cannot yet delete. A person who receives it and then asks for erasure has handed you a problem you cannot solve.

6. Stores​

  • Apple App Review Guideline 5.1.1(v): an app that lets users create an account must let them delete it inside the app.
  • Google Play: an in-app deletion path and a web link where people can ask for deletion without reinstalling. The link goes in the Data safety form. Data you keep must be disclosed.
  • If deletion goes through a queue, the app must tell the user the timeline.

Sources, as of October 2026: Apple App Review Guidelines, Google Play account deletion requirements.

Rules​

Rule

Every erasure is a tracked request with steps, not a fire-and-forget event. A subscriber that throws once leaves a half-deleted account, and nobody finds out.

Rule

Erasure must reach processors and file versions, not only our tables. Deleting our row while the auth provider, analytics tool and old file versions keep the data is not erasure.

Rule

Staff cannot change a user's consent or data on a hunch. They act on a verified request and every action is logged.

Mistakes we made​

  • Instant deletion with no checklist. The delete button erased immediately and published an event. The in-process event bus logged a failing subscriber and never retried. Token revoke, file deletes and the AI-service relay could each fail silently. The fix was a request queue where every module's sweep is a visible step with Retry.
  • Many tables had no erase at all. Journals, highlights, saved items, reminders, onboarding answers, moderation reports and user audit actors survived deletion, while the policy promised deletion. Generate the sweep list from the data map and test it.
  • A misleading checklist step. The "review known gaps" step listed only five modules and implied one module was already erased. Staff ticking it would have missed several. Step text is operator instructions; review it like code.
  • Avatars unlinked, not deleted. The profile pointer was cleared but the object stayed, in a versioned bucket. Delete the object and all its versions.
  • Third parties never got a deletion call. The auth provider kept email and social ids forever; analytics, attribution and crash tools were never told. Add a step per processor.
  • The deletion URL in the policy did not exist. The policy linked to one path; the app route was another. Store reviewers click that link. Test it on every policy release.
  • Ad-audience uploads could not be undone. Nobody recorded who was in which upload. Keep an export log so a deletion can generate a removal step.

Checklist​

  • Request table with due_at; alerts before the deadline.
  • Steps generated from the data map, including processors and file versions.
  • Every sweep is idempotent and reports success or failure.
  • Identity checked in proportion; no account-existence leak.
  • Account locked during the deletion window; emails before and at completion.
  • Tombstone, if any, is keyed, short-lived and disclosed.
  • Export is async, JSON plus media, 7-day expiry, rate-limited and audited.
  • In-app deletion and a web deletion link exist; app states the timeline.
  • Policy's deletion link works.