← back to all posts
Privacy· GDPR· 5 min read

Answering a GDPR Article 15 access request from recorded history

An Article 15 request asks for the personal data you hold, its sources and its recipients. This post shows the queries that collect it from recorded history.

You know the drill. A former user emails your DPO inbox. Per Article 15 of the GDPR, they want a complete copy of every personal datum you hold on them. They want it within thirty days. They want it to include the source, the purpose, the recipients with whom you've shared it, and the logic of any automated decisions. They have a lawyer.

Your data is in fifteen places — Postgres, S3, Snowflake, Salesforce, Zendesk, Mixpanel, Stripe, your own product database, your CRM, your support tool. Even the engineer who built the integration to Klaviyo three years ago doesn't remember which field carries the consent flag. Reconciling all of it by hand takes two senior engineers about a week. Outside privacy counsel bills six hundred euros an hour. If you miss one source, the Data Protection Authority can fine you up to four percent of global revenue.

The math says one DSAR costs about €35,000. Big EU companies field a hundred or more a year. That's a real engineering team's budget, gone to evidence reconciliation.

What the engineering work actually is

If you trace what makes a DSAR slow, it's the same three things every time:

  1. Subject reconstruction. "Find every record about this person." Trivial if your data is in one graph, very hard if it's in fifteen systems with subtle ID mismatches.
  2. Purpose binding evidence. "Prove you used my data only for what you said." This requires checking the purpose of each read at the point of access.
  3. Automated decision lineage. Article 22 lets the user demand the logic behind any automated decision that affected them. Most ML systems can't produce this — the model card lives in MLflow, the input features live in a feature store, the rule that fired isn't logged.

If you had a single bitemporal graph that stored all of it natively, none of this would be hard. You'd write one Cypher query.

The one-query answer

MATCH (u:User)
WHERE u.email = $subject
OPTIONAL MATCH (u)-[:PROFILE_ATTR]->(p)
OPTIONAL MATCH (u)-[:GENERATED]->(e:Event)
OPTIONAL MATCH (u)-[:AUTOMATED_DECISION]->(d:Decision)
OPTIONAL MATCH (u)-[:SHARED_WITH]->(proc:Processor)
RETURN u, collect(p), collect(e), collect(d), collect(proc)

The query returns what the graph holds for the user: the profile attributes, events, automated decisions and downstream processors that your systems wrote to it. Data that was never written to the graph is not returned. Add AT RECORDED to each MATCH clause to run the same query as the data was recorded at a point in the past — useful when the lawyer asks what did you know about my client on March 15?

For Article 5(1)(b), field-level ACLs can require a purpose claim on the caller's JWT and deny reads whose purpose is not on the field's list (the property is returned as null). db.aclEvents returns the Cypher reads that field ACLs or purpose rules denied, and the searches and node creates they refused, with the principal, label, property, action and reason. Allowed reads are not logged, so the log shows enforcement, not a full access history.

Article 22 — the right-to-explanation — is a graph walk. db.derivedFrom on the decision node follows its DERIVED_FROM edges to the inputs recorded for it: the user's profile, the model version, the firm's policy in force, the events from the past week.

Article 17 — the right to erasure — is harder, because the audit log has to survive the erasure. Crypto-shred (per-subject encryption keys and key destruction) is in development. Contact us before relying on it for erasure requests.

The numbers

The figures in this post and in the walkthrough are illustrative. How long the queries take on your data depends on how many records connect to the subject.

You can watch the whole flow in the illustrative walkthrough on the Privacy tab. It plays scene by scene, with the narration shown as text.

See the walkthrough

Step through the illustrative walkthrough.

The full illustrative flow, ending with the chain verification. It is pre-rendered, runs in any browser, and does not connect to an engine.

Open the Privacy walkthrough →