Building a consent audit trail that survives a regulator
Storing a boolean is not evidence. Here is the record structure that answers the questions actually asked during an investigation.
The demonstrability requirement in GDPR Article 7(1) is short enough to fit in a sentence and expensive enough to fail an entire programme. The question that arrives is never “do you have consent?” It is “prove that this person, on this date, consented to this purpose, and show me what they were shown when they did.”
What a defensible consent record contains
Anything less than this and you are reconstructing from logs under deadline pressure.
{
"eventId": "cns_01J8XR4M2K",
"subject": { "id": "usr_8842", "idType": "internal", "hashedEmail": "9f2b…" },
"purpose": "marketing_email",
"decision": "granted",
"legalBasis": "consent",
"jurisdiction": "DE",
"regime": "GDPR",
"collectedAt": "2026-06-11T09:14:22.418Z",
"expiresAt": "2027-06-11T09:14:22.418Z",
"source": {
"channel": "web",
"property": "www.example.com",
"url": "https://www.example.com/pricing",
"experienceId": "banner_eu_v7",
"experienceHash": "sha256:41ac…",
"locale": "de-DE"
},
"interaction": { "action": "granular_save", "toggledPurposes": ["marketing_email"] },
"proof": { "ipTruncated": "203.0.113.0", "userAgentFamily": "Chrome/141" },
"supersedes": "cns_01J4KP9A7B"
}The five fields people forget
- 1experienceId and experienceHash. Without a pointer to the exact rendered experience, you cannot show what was agreed to. The hash matters because copy gets edited in place more often than anyone admits.
- 2regime. The rules that applied at collection time, frozen. Jurisdiction alone is not enough — regimes change, and a 2024 record was collected under 2024 rules.
- 3supersedes. Consent is a chain, not a value. The current state is derivable; the history is what proves the chain was never broken.
- 4expiresAt. Several regulators expect consent to be refreshed. If your records have no expiry, every one of them is arguably stale.
- 5interaction.action. "accept_all" and "granular_save" are materially different evidence. One shows a considered choice; the other shows a click.
Do not overwrite
The most common architectural mistake is an UPDATE on a consent row. Consent records are append-only. The current state is a projection over the event log — never the log itself.
Retention: the uncomfortable asymmetry
You must keep consent evidence long enough to defend the processing, while minimising personal data. These pull in opposite directions, and the resolution is to keep the record but reduce its identifiability.
- Truncate IP addresses at collection. The final octet adds no evidential value and considerable risk.
- Store a user-agent family, not the full string — the full string is a fingerprint.
- Keep the record for the statute of limitations on the processing, then anonymise rather than delete, so aggregate proof survives.
- Never store the raw email in the consent event when a stable internal ID plus a hash will do.
The four queries to test against
A consent store is only as good as the questions it can answer quickly. Time each of these; if any takes a person more than a few minutes, the design is not finished.
- 1Full consent history for one identity, across every channel and system.
- 2Every identity whose consent for a given purpose was collected under a specific banner version.
- 3The population whose consent expires in the next 30 days.
- 4The exact rendered experience for one event, reproduced pixel-for-pixel.
An audit trail you have never queried under pressure is a hypothesis, not a control.
Run the drill before someone else does. Pick a random identity, hand it to whoever would actually field the request, and time the answer end to end. The result is usually the most useful privacy metric you will collect all quarter.