DSAR Without Drama: Right-to-Access and Erasure in the Tracker
Articles 15 and 17 don't pause for analytics. Here's how to architect right-of-access and erasure endpoints that don't break your rollups — and what we shipped.
This is privacy research, not legal advice. See the footer for the full disclaimer.
TL;DR
- Articles 15 and 17 apply to web analytics — pseudonymous identifiers like hashed IPs, cookie IDs and BLAKE3-HMAC visitor signatures remain personal data per the Breyer, IAB Europe and Brussels Court of Appeal 14 May 2025 line.
- Three endpoints shipped —
POST /api/privacy/opt-out,GET /api/privacy/access,POST /api/privacy/erase— all CSRF-protected, rate-limited, audit-logged. - Rollup tables survive erasure because they have already been aggregated past the point of identifiability —
uniqCombined64HyperLogLog sketches are counts, not records, and individual contributions cannot be reversed. - 8 privacy audit events emit structured Article 28(3)(h) accountability evidence — covering opt-out, access, erase, consent given/withdrawn, and legal-page views.
- EDPB 2025 enforcement focus was the right to erasure under the coordinated framework; EDPB 2026 focus shifts to transparency obligations. Both align with the architecture in this post.
Why analytics is the most-forgotten DSAR surface
Most GDPR Data Subject Access Request workflows are built around the systems where personal data obviously lives — the CRM, the help-desk tickets, the order history, the email-marketing lists. The web analytics stack is usually an afterthought, and when it does come up, the operator’s first instinct is often “there’s no personal data there, we hashed the IPs.” That instinct is wrong on the law and increasingly wrong on the architecture.
The draft EDPB Guidelines 01/2025 on Pseudonymisation — adopted as a draft at the 101st plenary on 16 January 2025, open for public consultation through 28 February 2025, and still in development as of May 2026 — clarify that pseudonymised data, including hashed IPs, cookie IDs, BLAKE3/HMAC visitor signatures, and TC strings, remains personal data when re-identification is reasonably likely via means available to the controller or any third party. The CJEU’s IAB Europe judgment (C-604/22, 7 March 2024), confirmed by the Brussels Court of Appeal judgment of 14 May 2025 (case 2022/AR/292), extends this beyond TC strings to any identifier paired with an IP. Breyer (C-582/14, 19 October 2016) settled the operator-IP question much earlier.
Practically, this means: web analytics that processes IPs, hashed cookies, BLAKE3-HMAC visitor signatures, or any per-visitor identifier is processing personal data within the meaning of the GDPR. Articles 15 (right of access) and 17 (right to erasure) apply. Article 28(3)(e) requires the processor — if there is one — to assist the controller with these requests. The Article 33 breach-notification clock includes breaches affecting these identifiers.
The post that follows is the operator’s how-to. The anatomy of an analytics DSAR, the three endpoints Statnive Live ships, the audit trail required, how rollups survive erasure, a developer-friendly DSAR runbook, and a working code sample.
Anatomy of an analytics DSAR
A DSAR against a web-analytics stack does not look like a DSAR against a CRM. There is no email address, no customer name, no obvious primary key. The data subject reaches out and says, in effect: “I visit your site sometimes. What do you have on me, and please delete it.”
What does the operator actually have?
For a cookieless first-party analytics stack like Statnive Live in consent-free mode, the answer is: a daily-scoped pseudonymous signature derived from the visitor’s IP, User-Agent, site-scope and a daily-rotating salt that is destroyed at end of day. The architecture is described in detail in the 2026 EU Consent-Free Analytics Playbook. The day’s signature exists in the raw events table until the salt rotates at the next 00:00 UTC; after rotation, the signature becomes effectively irreversible because the salt that produced it is gone. Rollups aggregate that signature into counts before the salt rotation, so the per-visitor identifier is not present in the rollup tables at all — only counts of unique visitors per day, page or source.
For a consent-required or hybrid Statnive Live deployment with consent given, additionally a cookie ID is emitted to the browser (a raw UUID). The server stores SHA-256(master_secret || site_id || cookieID) with an h: prefix as the persistence key. Cross-day visitor continuity is preserved by the hashed cookie ID rather than by the daily-rotating salt.
Three things follow from this:
- Article 15 access. The data subject can give the operator something to look up — typically the cookie ID from their browser (if it exists in their current session) or a description of their visit (date, page, approximate time, IP from a known network). The operator can return the events that match.
- Article 17 erasure. The operator can locate the same events and delete them. The records the operator can identify are the records the operator can erase.
- What cannot be identified cannot be erased — and cannot be retained either. The daily salt destruction at 00:00 UTC is a contemporaneous, automatic erasure of cross-day re-identification capability. A visitor’s session-day-1 signature is permanently unlinked from their session-day-2 signature once Day 1’s salt is destroyed; an Article 17 request reaching the operator on Day 2 cannot ask for the deletion of Day 1’s records because they cannot be located, but that is also fine because they are no longer re-identifiable.
The architecture is designed so that the steady-state retention of identifiable data is small. The salt rotation does most of the GDPR Article 5(1)(c) data-minimisation work for free. DSAR requests against an analytics stack architected this way are intrinsically lower-volume than DSAR requests against a CRM — most visitors leave no per-visitor record at all once the salt rotates.
The three endpoints
Statnive Live ships three privacy endpoints by default. All three are CSRF-protected, rate-limited, and emit structured audit events.
POST /api/privacy/opt-out
The Article 21 right-to-object endpoint. A visitor calls this from a button in the operator’s privacy policy. The server registers the opt-out as a strictly-necessary cookie expressing the user’s choice (permitted under ePrivacy § 25(2)(ii) / TDDDG / equivalent national transpositions because it implements the user’s expressly-requested service preference). Subsequent ingest requests from the same browser are dropped at the ingest layer before the visitor signature is computed.
Alternatively, the opt-out can be persisted server-side via a suppression list keyed on the visitor’s hashed signature with an adequate TTL — the choice is operator-configurable.
The endpoint emits the privacy.opt_out_received audit event with the timestamp, site ID and a hash of the visitor’s signature for cross-reference with the audit log.
GET /api/privacy/access
The Article 15 right-of-access endpoint. The visitor provides:
- The cookie ID currently stored in their browser (if any), or
- A signed pre-shared identifier (for operators integrating with their own DSAR portal), or
- A description of their visit narrow enough that the operator can locate it (date, page, approximate time, source network).
The endpoint returns the events on file. The response is a JSON object with the raw events that match the lookup, plus the rollup-derived metadata that touches the visitor (which rollup buckets their visit contributed to, without disclosing other visitors in the same bucket). Audit event: privacy.dsar_access_requested.
POST /api/privacy/erase
The Article 17 right-to-erasure endpoint. The lookup is the same as Article 15. The endpoint enumerates the storage backend’s tables dynamically — via system.tables introspection on ClickHouse — and deletes the matching rows from every table that has a visitor_signature or cookie_id_hash column. The dynamic enumeration is intentional: a future table added to the schema fails closed if it carries a visitor identifier but is not added to the erasure path, because the integration test that asserts the dynamic enumeration covers every table the schema knows about.
Aggregate rollups are not deleted. The reason — and it is the GDPR-correct reason — is in the next section. Audit event: privacy.dsar_erase_requested.
All three endpoints reject unsigned cross-origin requests, require a CSRF token derived from the operator’s session, and rate-limit per IP and per (IP, site_id) tuple to prevent abuse. The audit log is separate from the analytics database and retained per the operator’s audit-log retention policy — typically longer than the 25-month analytics rollup window.
Why rollups survive erasure — and why that’s correct
The single most-counterintuitive part of analytics-DSAR design is the survival of the aggregate rollup tables across an Article 17 erasure request. The first reaction is usually: if the user wants to be deleted, surely all data about them must go, including the counts they contributed to?
The answer turns on what the rollup table actually contains. A row in hourly_visitors records, for example: site_id=X, hour=Y, unique_visitors=4271. The row does not contain the visitor’s signature, the visitor’s IP, or any per-visitor identifier. It contains a uniqCombined64 HyperLogLog sketch — a probabilistic data structure that produces accurate cardinality estimates without storing individual values. The uniqCombined64 sketch is a count, not a record of who. The visitor’s contribution to the count cannot be reversed out of the sketch.
This is the standard EDPB position on aggregated data: once data has been aggregated past the point of identifiability — past the point where any individual contribution can be reconstructed — it is no longer personal data. The CNIL Sheet 16 recommendation to aggregate to nearest 10 is grounded in the same principle. The Italian Garante’s anonymisation analysis in its Caffeina Media decision turns on whether re-identification is reasonably likely; for an aggregate count derived from a HyperLogLog sketch, it is not.
The practical effect: an Article 17 erasure request deletes the raw event rows (which carry the per-visitor signature, the IP-derived geolocation, the host-only referrer, the page) but leaves the rollup tables intact. The operator’s dashboards continue to show the historical traffic to the page, but no row in the dashboard can be traced back to the data subject who requested erasure.
This is described in the operator’s privacy policy as: “aggregated past the point of identifiability.” That is the right wording — neither over-claiming “anonymous” nor under-claiming “still personal data”. The rollups are the GDPR-correct steady state for analytics retention.
The 25-month ceiling on the rollup TTL (750 days, via the 011_rollup_ttl.sql migration) applies regardless. Even the aggregate counts expire automatically on the CNIL Sheet 16 timeline.
The audit trail — 8 privacy events
Statnive Live emits 8 structured audit events covering the privacy-and-legal surface. The events are the Article 28(3)(h) accountability evidence for an audit by the operator’s DPO, the controller’s auditor, or a data-protection authority:
| Event | Trigger | Purpose |
|---|---|---|
privacy.opt_out_received | POST /api/privacy/opt-out | Article 21 objection registered |
privacy.dsar_access_requested | GET /api/privacy/access | Article 15 request initiated |
privacy.dsar_erase_requested | POST /api/privacy/erase | Article 17 request initiated |
privacy.consent_given | statnive.acceptConsent() | Consent granted in consent-required / hybrid mode |
privacy.consent_withdrawn | statnive.withdrawConsent() | Consent revoked |
legal.lia_viewed | GET /legal/lia | Operator’s LIA page served |
legal.dpa_viewed | GET /legal/dpa | Article 28 DPA page served |
legal.privacy_policy_viewed | GET /legal/privacy-policy/{lang} | Privacy policy page served |
Each event is structured JSON with timestamp, site ID, request ID, and the relevant fingerprint. The audit log is separate from the analytics database — it does not roll up, it does not expire on the 25-month analytics TTL, and it is retained per the operator’s audit-log policy (typically longer, often indefinitely for compliance evidence).
The audit trail is what an operator hands to a regulator on inspection. The privacy policy is the public-facing position; the audit events are the contemporaneous evidence the position was actually implemented. The CNIL inspection response pack typically includes (a) the LIA, (b) the privacy policy, (c) the audit log for the inspection window, and (d) the event-audit endpoint output (the 3-event ceiling check for French deployments). Statnive Live produces all four out of the box.
DSAR runbook for operators
A working runbook for handling a DSAR against a Statnive Live deployment:
Step 1: Identify the request. The data subject reaches out, typically by email to the controller’s published DSAR address. The request must be specific enough that the operator can locate the data — typically that means the cookie ID currently in the visitor’s browser (which the visitor can inspect via DevTools) or a description of the visit narrow enough to identify the event window.
Step 2: Verify the requester’s identity. GDPR Article 12(6) allows the controller to request additional identification where reasonably necessary to verify the identity of the data subject. For analytics requests, the proof tends to be: control of the cookie ID (the visitor sends a screenshot of DevTools showing the cookie), or control of the IP the visitor visited from (an authenticated login from the same IP, or a callback challenge). The operator should not require more identification than is necessary to verify the request.
Step 3: Run the access query. The operator calls GET /api/privacy/access with the lookup identifier. The response is the matching events. If the request is an Article 15 access request, this is the response to send to the data subject — formatted as a CSV, a JSON, or a PDF, as the operator’s DSAR portal prefers.
Step 4: Run the erasure (if requested). The operator calls POST /api/privacy/erase with the same lookup identifier. The endpoint deletes the matching rows from every table that has a visitor identifier; rollup tables are left intact (per the previous section). The endpoint returns a count of rows deleted per table.
Step 5: Confirm to the data subject. GDPR Article 12(3) allows up to one month to respond, extendable by two more months for complex requests. The operator’s response should:
- Confirm the data was located and (if erasure was requested) deleted.
- Describe what was retained — the rollup aggregates — and why they are not personal data.
- Note the operator’s audit log retention (the fact that the DSAR request itself was logged).
Step 6: Audit-log the response. The Statnive Live audit events were emitted automatically when the operator called the API endpoints. The operator should also log the response sent to the data subject in their broader DSAR-handling system (typically the help-desk or DPMS).
The whole runbook fits in a half-page checklist. Most operators implementing it for the first time worry that the analytics DSAR will be hard; in practice, the architecture is designed so that the data either exists in a small, well-defined window or does not exist at all. The volume is low, the queries are bounded, and the response shape is standardised.
A working integration sample
For operators integrating their own DSAR portal with Statnive Live, the following pattern works end-to-end:
// Triggered from a DSAR portal after identity verification
async function handleAccessRequest(verifiedRequest) {
const response = await fetch(
`https://app.statnive.live/api/privacy/access?site_id=${SITE_ID}&cookie_id=${verifiedRequest.cookieId}`,
{
method: 'GET',
headers: {
'X-Statnive-CSRF': await getCsrfToken(),
'X-Statnive-Operator-Key': OPERATOR_API_KEY,
},
}
);
if (!response.ok) {
throw new Error(`DSAR access failed: ${response.status}`);
}
const { events, rollup_metadata } = await response.json();
return { events, rollup_metadata };
}
async function handleErasureRequest(verifiedRequest) {
const response = await fetch(
`https://app.statnive.live/api/privacy/erase`,
{
method: 'POST',
headers: {
'X-Statnive-CSRF': await getCsrfToken(),
'X-Statnive-Operator-Key': OPERATOR_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
site_id: SITE_ID,
cookie_id: verifiedRequest.cookieId,
}),
}
);
if (!response.ok) {
throw new Error(`DSAR erasure failed: ${response.status}`);
}
const { rows_deleted_by_table } = await response.json();
return { rows_deleted_by_table };
}
The operator key is rotated per the operator’s secret-rotation policy. The CSRF token is derived from the operator’s session as part of the standard Statnive Live admin auth flow.
What a self-hosted deployment changes
For operators running Statnive Live as a self-hosted deployment — the binary running on the operator’s own infrastructure — the controller/processor analysis changes. Statnive is not a processor of the operator’s analytics data at all; the operator is the sole controller. There is no Statnive ↔ operator DPA to sign because there is nothing for Statnive to be a processor of.
The DSAR endpoints work identically on a self-hosted binary as on a hosted Statnive Live SaaS deployment. The audit events emit to the operator’s chosen log sink (stdout / stderr in containerised deployments, syslog on traditional hosts, or a dedicated audit table in self-hosted setups). The integration sample above works the same way against the operator’s own endpoint.
What differs: the controller’s response to an Article 15 request from a third-party data subject is the controller’s response only. There is no Statnive sub-processor to assist; the operator’s IT team is the operator’s DSAR responder.
This is one of the trade-offs covered in the self-hosted vs private EU SaaS post. For operators with strict no-third-party-touches-the-data postures (regulated industries, air-gapped environments), the self-hosted shape is the clean answer; for operators with strict signed-processor-agreement postures (ISO 27001 vendor questionnaires, large procurement), the SaaS shape with an Article 28 DPA is the clean answer. The DSAR architecture works the same way in both.
What this gives the operator
The practical outcome:
- A clean DSAR response pack ready for any Article 15 / 17 request against the analytics layer. Three endpoints, eight audit events, a runbook, and an integration sample.
- A defensible answer on rollup survival. The “aggregated past the point of identifiability” language is the EDPB-aligned position; the 25-month rollup TTL applies regardless.
- An accountability trail that maps to Article 28(3)(h) audit assistance for processor relationships and the broader Article 5(2) accountability principle for the controller’s own records.
- Forward compatibility with the Digital Omnibus Article 88a’s data-subject-rights expectations. The current text does not change the substantive Articles 15 and 17 obligations; the DSAR endpoints work unchanged.
What it does not give: a way to retain raw per-visitor data beyond the 25-month rollup window, because the salt destruction makes that impossible. A way to “undo” a delete request, because the deletions are irreversible. A way to skip the audit log for “internal” requests, because there are no internal requests — every call to the three endpoints emits an event.
What to do, and what to skip
| Do | Don’t |
|---|---|
| Treat hashed visitor identifiers as low-risk personal data; honour Articles 15, 17 and 21 against them. | Market hashed identifiers as “anonymous” — pseudonymous is the legal correct word; the Brussels Court 14 May 2025 confirmed it. |
Wire the three privacy endpoints (/api/privacy/opt-out, /api/privacy/access, /api/privacy/erase) with CSRF protection, rate limiting, and audit logging. | Build a one-off DSAR handler that bypasses audit logs — Article 28(3)(h) accountability is the evidence pack you need on inspection. |
| Describe rollup-table survival under Article 17 as “aggregated past the point of identifiability” — neither overclaiming “anonymous” nor underclaiming “personal data.” | Delete rollup-table rows in response to an Article 17 request — they’re aggregate counts, not per-visitor records, and you’d lose historical traffic visibility for no GDPR benefit. |
| Verify the requester’s identity per GDPR Article 12(6) — typically by control of the cookie ID or a callback challenge — before running the access / erase query. | Demand more identification than is necessary; the EDPB 01/2022 Right of Access guidance is explicit that overreach defeats the right. |
| Document the runbook + log every privacy audit event — EDPB 2025 CEF focus was right to erasure, EDPB 2026 CEF focus is transparency. | Treat DSAR handling as one-off operational work — coordinated DPA enforcement is active, and a contemporaneous audit trail is the right answer. |
The bottom line
Right-of-access and right-to-erasure under GDPR Articles 15 and 17 apply to web analytics. The EDPB Guidelines 01/2025, the CJEU Breyer and IAB Europe judgments, and the consistent national-regulator position all say so. The architecture decision is what to do about it — and the answer is to make the per-visitor data window small (daily salt rotation), make the rollup window bounded (25-month TTL), expose three endpoints (opt-out, access, erase), emit eight audit events, and document the position in the privacy policy.
Statnive Live ships this surface by default. Operators integrate against it; the audit log captures the contemporaneous evidence; the rollup tables survive erasure because they have already been aggregated past identifiability. The DSAR drama, when it arrives, is procedural — a one-month clock, a verification step, an API call, a confirmation email — not architectural.
For the broader privacy frame, see the 2026 EU Consent-Free Analytics Playbook. For why the daily-rotating salt makes the architecture intrinsically minimal, see the country-by-country map. For how the same audit events power the GPC and hybrid-mode integration, see the linked post. The DSAR surface is the connective tissue across all four.
This is privacy research, not legal advice. The DSAR endpoints described are technical-availability features. The legal correctness of any response to an Article 15 / 17 / 21 request is the controller’s responsibility under GDPR Article 12 and the relevant national legislation. Every Statnive customer remains the data controller and bears responsibility for its own configuration and DPIA. Cross-reference with qualified counsel in your jurisdiction before publication.
Status of regulatory references as of 13 May 2026: GDPR Articles 12, 15, 17, 21, 28(3)(e), 28(3)(h), and 33; EDPB Guidelines 01/2022 on Right of Access (final version adopted March 2023); CJEU C-582/14 Breyer of 19 October 2016 (ECLI:EU:C:2016:779); CJEU C-604/22 IAB Europe of 7 March 2024; Brussels Court of Appeal judgment of 14 May 2025 in case 2022/AR/292 (confirms IAB Europe / TC String line); draft EDPB Guidelines 01/2025 on Pseudonymisation (adopted as draft at 101st plenary 16 January 2025; public consultation through 28 February 2025; final not yet published as of May 2026; EDPB sprint team targets summer 2026 completion); EDPB Guidelines 1/2024 on Legitimate Interest of 8 October 2024; EDPB 2025 coordinated enforcement framework focus: right to erasure (Article 17); EDPB 2026 coordinated enforcement framework focus: transparency and information obligations (Articles 13/14); ePrivacy Directive 2002/58/EC (in force) and its Member-State transpositions including § 25 TDDDG (Germany) and Article 82 Loi 78-17 (France).