GDPR Compliance Guide
Consenti is built from the ground up around GDPR requirements. All core design decisions follow the six lawfulness principles in Article 5.
opt-in — covers GDPR (EU / EEA), UK GDPR, PDPA-TH (Thailand), and KVKK (Turkey) by default. PIPEDA (Canada), POPIA (South Africa), and APPI (Japan) auto-resolve to the more permissive general-privacy-consent group instead — see the Compliance Groups page for why — but their own regulation pages recommend opting into opt-in for stronger alignment. Use compliance: { type: 'opt-in' } in your ConsentiSetup config to activate the opt-in Compliance Group explicitly.Official references
- Regulation (EU) 2016/679 — full text on EUR-Lex
- European Data Protection Board (EDPB) — guidelines, recommendations, binding decisions
- ICO — UK GDPR guidance (post-Brexit UK equivalent)
- EDPB Guidelines 05/2020 on Consent
Key requirements and how Consenti meets them
| Requirement | Implementation |
|---|---|
| Freely given | No content gating by default — the initial banner never blocks page content or trackers, and overlay opacity is 0 unless explicitly configured. Content gating (blocking the page behind an overlay until a choice is made) is available as an advanced, opt-in operator setting for sites that need it — use deliberately, since forced gating can itself undermine "freely given" consent under GDPR. |
| Specific | Per-cookie granularity — each cookie has its own consent entry in consentJson |
| Informed | htmlText on categories, legitimateInterestDescription for LI basis |
| Unambiguous | No pre-ticked boxes; all non-mandatory cookies default to 'denied' |
| Easy to withdraw | The preference modal (widget.showModal()) lets a visitor change or revoke any category with the same number of clicks as granting — that UI is the Art. 7(3) implementation. The erase API below is controller tooling for full-account deletion, not the visitor-facing withdrawal path. |
| Records kept | consent_history table (retained per purgeAfterDays, deleted on erasure — see below); audit_logs for admin actions (append-only, never deleted by Consenti) |
Right to erasure (Article 17)
DELETE /consenti/api/v1/consent/:visitorIdThis deletes:
- All entries in
consent_recordsfor the visitor ID - All entries in
consent_historyfor the visitor ID - The
visitorsrecord (IP hash, UA hash, geolocation)
The visitor ID itself is a random UUID and contains no PII.
For how to give visitors a self-service "Forget me" button in the widget itself, the events both sides fire, and how this fits together as one flow, see the Right to Erasure guide.
Data minimisation (Article 5(1)(c))
- IP addresses are stored as SHA-256 hashes only — the raw IP is never persisted
- User agents are stored as SHA-256 hashes only
- Consent records contain only: visitor UUID, profile ID, consent choices, timestamp, source
Consent records as evidence
Every consent record includes:
profileId— the exact profile the user consented under. A new id is minted on every profile edit (the previous one is kept, archived, as history), so this alone identifies which cookie policy version was in effect — no separate version counter neededcreatedAt/updatedAt— exact ISO 8601 timestampssource—'banner','api', or'import'consent_history— a change-log of every update to the record; retained perpurgeAfterDaysand deleted along with the record on erasure (distinct fromaudit_logs, which is append-only and never deleted by Consenti)
Legitimate Interest (Article 6(1)(f))
Configure a category with legalBasis: 'legitimate_interest' in your profile — legal basis is set once per category, and every parameter listed in cookies inherits it:
{
"preferenceModal": {
"categories": {
"marketing": {
"heading": "Marketing",
"htmlText": "Relevant ads based on your activity.",
"legalBasis": "legitimate_interest",
"legitimateInterestDescription": "We show relevant ads under legitimate interest.",
"cookies": ["ad_personalization"]
}
}
}
}Status values for LI parameters:
"granted"— user did not object"objected"— user exercised their right to object (GDPR Art. 21)
'objected' status is only valid for parameters whose category has legalBasis: 'legitimate_interest'. The server rejects 'objected' for standard consent-basis parameters.Re-consent when the profile changes
Profiles have no in-place version counter — every edit mints a brand-new Profile.id and archives the previous one as history. The widget's verify endpoint compares the consent's profileId against the compliance group's currently active profile id and returns { valid: false, reasons: ['profile_changed'], currentProfileId, consentProfileId } when they differ, triggering the banner to show again.
Banner requirements
- Do not set
overlayOpacityabove 0 if users can't access site content without consenting - Always provide a way to withdraw consent (use
BannerTriggerorwidget.showModal()from a footer link) - Mandatory categories must be truly necessary — don't put analytics parameters in a
legalBasis: 'mandatory'category