All 20 checks with why-it-matters prose, severity, and cross-references to related audits.
GDPR Article 7 requires you to demonstrate that consent was freely given, specific, informed, and unambiguous — a boolean flag proves none of this. Under GDPR Article 30, you must maintain records of processing activities. A single `subscribed BOOLEAN` column cannot tell a regulator when consent was given, under which version of your privacy policy, from which collection point, or for which communication scope. CCPA §1798.100 similarly requires you to substantiate what personal data you hold and how it was lawfully obtained. When a data protection authority opens an investigation, a boolean flag is not evidence — it is a liability.
Why this severity: Critical because a boolean flag leaves you unable to demonstrate lawful basis for processing under GDPR Art. 7, exposing you to enforcement action and fines up to 4% of global annual turnover.
compliance-consent-engine.consent-storage.structured-recordsSee full patternGDPR Recital 43 and Article 7 require that consent be specific — a contact agreeing to order confirmations has not agreed to promotional campaigns. ePrivacy Article 5(3) separately governs marketing communications, requiring its own specific consent. CCPA §1798.120 gives consumers the right to opt out of specific categories of data sale and sharing. A single `subscribed` flag collapses distinct legal bases into one undifferentiated bucket, making it impossible to honor scoped opt-outs without silencing the contact entirely. When a contact withdraws consent for marketing but not transactional emails, a system with no scope differentiation either keeps sending marketing or breaks transactional delivery.
Why this severity: High because conflating consent scopes violates GDPR Art. 7's specificity requirement and forces a binary all-or-nothing opt-out that breaks transactional email when a contact opts out of marketing.
compliance-consent-engine.consent-storage.granular-preferencesSee full patternGDPR Article 5(2) requires you to demonstrate accountability for all processing decisions — an audit trail you can retroactively rewrite is not an audit trail. Article 7(1) requires you to demonstrate that consent was given; if the row showing consent was later updated, you have no proof of the original grant. CWE-472 (External Control of Assumed-Immutable Web Parameter) captures the class of vulnerability where data assumed to be authoritative can be silently altered. A consent record that can be `UPDATE`d can be altered after the fact — by a rogue employee, a compromised service account, or a migration that inadvertently clears historical grants — leaving you unable to prove lawful basis for past sends.
Why this severity: Critical because mutable consent records allow retroactive alteration of the legal basis for historical sends, making it impossible to demonstrate GDPR Art. 7(1) compliance and exposing the business to fraudulent erasure of withdrawal records.
compliance-consent-engine.consent-storage.immutable-recordsSee full patternA consent table with no index on `contact_id` requires a sequential scan for every pre-send consent check. At 100,000 contacts with an average consent history of 3 records each, that is 300,000 rows scanned per recipient per campaign — multiplied across batch send volumes, this degrades send throughput and can cause pre-send checks to time out, forcing code paths that skip the check entirely to meet send deadlines. ISO 25010:2011 performance efficiency directly covers this: the system must perform the correct function (consent verification) within the required time under the stated load.
Why this severity: Low because the operational impact (slow sends) is real but the compliance risk is indirect — the consent check still runs, it is just slow enough to become a reliability pressure that leads teams to skip it.
compliance-consent-engine.consent-storage.indexed-querySee full patternGDPR Articles 13 and 14 require that consent be informed — the contact must know what they are agreeing to at the time they agree. If your privacy policy changes substantially and you cannot show that pre-existing consents were obtained under the old policy (not the new one), you cannot determine whether those consents remain valid for processing under the updated terms. CCPA §1798.100 similarly requires you to be able to characterize what data was collected and under what notice. A `policy_version` field is the mechanism that makes this determination possible; without it, every privacy policy update potentially invalidates your entire consent corpus.
Why this severity: Medium because the gap only becomes an enforcement problem after a material privacy policy change, but by then the historical consents are already unverifiable and re-collection from your entire list may be required.
compliance-consent-engine.consent-storage.version-trackingSee full patternGDPR Article 7(3) requires that withdrawal of consent be as easy as giving it — a synchronous unsubscribe handler that blocks on ESP API calls and multiple database writes makes withdrawal fragile. CWE-636 (Not Choosing the Least Risky Alternative) applies directly: performing all suppression work inline when a background job is available introduces unnecessary failure modes. CCPA §1798.120 imposes opt-out obligations without exception for system timeouts. If the ESP API is slow or unavailable when a contact clicks unsubscribe, a synchronous handler either times out (swallowing the opt-out entirely) or returns an error the user sees — both outcomes violate the spirit and often the letter of the regulation.
Why this severity: High because a synchronous opt-out handler that fails silently on ESP API timeouts leaves contacts legally opted out by intent but still reachable by the system, creating direct regulatory exposure under GDPR Art. 7(3) and CCPA §1798.120.
compliance-consent-engine.opt-out-processing.async-processingSee full patternCAN-SPAM §7704(a)(4) gives you 10 business days to honor an opt-out request — but without a `processed_at` timestamp, you cannot prove you met that window for any specific request. GDPR Article 7(3) requires withdrawal to be honored without delay. CCPA §1798.120 imposes similar obligations. CWE-778 (Insufficient Logging) applies: if opt-out processing timing is not measured and recorded, you have no way to detect when a queue backlog is pushing processing into violation territory, and no evidence of compliance when a regulator asks. The absence of timestamps turns a solvable reliability problem into an undetectable and undefendable one.
Why this severity: Critical because without `processed_at` timestamps and queue depth alerting, the system cannot demonstrate CAN-SPAM §7704(a)(4) compliance for any specific opt-out request, and queue failures accumulate silently until a regulator asks for evidence.
compliance-consent-engine.opt-out-processing.regulatory-windowSee full patternGDPR Article 7(3) states that a data subject has the right to withdraw consent at any time and that withdrawal must be as easy as giving consent — a global unsubscribe that silently leaves other campaign lists active violates this requirement. ePrivacy Article 5(3) applies separately to marketing communications. CCPA §1798.120 gives consumers the right to opt out of all sale and sharing, not selected categories. Multi-list email platforms (Mailchimp, Klaviyo, HubSpot) default to list-scoped unsubscribes, which means a contact who clicks the standard unsubscribe link in a newsletter will continue receiving promotional and partner emails unless the sending path explicitly checks for global suppression first.
Why this severity: High because a global opt-out that only suppresses one campaign type means the system continues sending to a contact who has exercised their GDPR Art. 7(3) or CCPA §1798.120 withdrawal right, creating direct regulatory liability.
compliance-consent-engine.opt-out-processing.global-unsubscribeSee full patternGDPR Article 7(3) and CCPA §1798.120 require opt-outs to be honored — a race condition between async opt-out processing and a campaign send batch is not an acceptable excuse for sending to a contact who withdrew consent. CWE-284 (Improper Access Control) captures the broader class: the system fails to enforce the access control decision (do not send) during the window between request and processing. A contact who unsubscribes at 11:58 PM and a campaign batch that starts at midnight will be sent a campaign if the only guard is the processed consent record, which the async worker has not yet written.
Why this severity: High because the race window between an opt-out request and async processing completion is predictable and exploitable by campaign send timing, creating measurable violations of GDPR Art. 7(3) and CAN-SPAM §7704(a)(4).
compliance-consent-engine.opt-out-processing.fail-safe-blockSee full patternGDPR Article 7(3) and CAN-SPAM §7704(a)(4) impose time-bounded opt-out obligations with no exception for queue backlog. When a large campaign send floods the job queue at the same time as incoming opt-out requests, a FIFO queue without priority differentiation can delay opt-out processing by hours — pushing real contacts into violation territory. Separately, if the campaign recipient list is built at scheduling time (hours before send), contacts who opted out after list construction will be in the send batch regardless of the queue's behavior. Both failure modes cause sends to contacts who have legally withdrawn consent.
Why this severity: Medium because queue backlog delays opt-out processing into regulatory violation territory under high send volumes, and stale recipient lists create a deterministic window where opted-out contacts are unreachable by the suppression system.
compliance-consent-engine.opt-out-processing.backlog-respectSee full patternGDPR Article 17 grants EU residents the right to erasure of their personal data — and it is not optional for any system that serves EU users. Without a coded deletion path, every right-to-erasure request requires manual database surgery by an engineer, which is slow, error-prone, and leaves no audit trail. CWE-212 (Improper Removal of Sensitive Information Before Storage or Transfer) covers the broader pattern of not handling PII lifecycle correctly. CCPA §1798.105 grants California residents similar rights. An untested deletion path is nearly as risky as no deletion path: without automated verification, the cascade almost certainly misses tables, leaving PII silently intact.
Why this severity: High because the absence of a coded, tested deletion path means every erasure request is a manual intervention with no audit trail, making GDPR Art. 17 and CCPA §1798.105 compliance undemonstrable and costly to operationalize.
compliance-consent-engine.data-subject-rights.gdpr-deletion-pathSee full patternGDPR Article 17(1) requires erasure of all personal data concerning the data subject — not just the primary contact row. CCPA §1798.105 similarly covers all personal information collected. Personal data in an email platform is routinely scattered across contacts, consent records, email event logs (with IP addresses and user agents), form submissions, and analytics events. Deleting the contacts row while leaving IP addresses and email addresses in event tables violates the right to erasure. CWE-459 (Incomplete Cleanup) and CWE-212 (Improper Removal of Sensitive Information) both apply: the deletion path exists but does not clean up the full surface area of PII.
Why this severity: High because a deletion that covers only the primary contacts table leaves PII in event log tables, directly violating GDPR Art. 17(1)'s requirement to erase all personal data — not just the contact record.
compliance-consent-engine.data-subject-rights.deletion-cascadeSee full patternGDPR Article 5(2) requires the controller to demonstrate accountability — when a data protection authority asks "did you process this erasure request?" you need a system-of-record answer, not a search through stdout logs or a best-effort reconstruction from the absence of data. GDPR Article 17 does not just require you to delete data; it implicitly requires you to prove you deleted it. ISO 27001:2022 A.5.28 covers evidence of control operation. A deletion that leaves no receipt is operationally indistinguishable from a deletion that never happened, and log files that get rotated before the DPA inquiry cannot serve as evidence.
Why this severity: Low because the erasure itself may be technically complete, but the absence of a durable receipt makes compliance undemonstrable to regulators and forces costly manual reconstruction from backup diffs during any audit.
compliance-consent-engine.data-subject-rights.deletion-receiptSee full patternCCPA §1798.100 and §1798.110 grant California residents the right to know what personal information a business has collected about them and to receive it in a portable format. GDPR Article 15 (right of access) and Article 20 (right to data portability) impose equivalent obligations for EU residents. Without a data export path, every access request requires a manual database query — slow, expensive, and inconsistent across requests. An export that only returns the contact profile while omitting consent history, engagement events, and form submissions is an incomplete response to a legal right, leaving the business exposed to regulatory action.
Why this severity: Low because the operational gap (no export tooling) only becomes a compliance violation when a data subject actually exercises their access or portability right — but that trigger is entirely outside your control.
compliance-consent-engine.data-subject-rights.ccpa-exportSee full patternGDPR Article 17(3) explicitly permits retention of data necessary for the purposes of archiving in the public interest, scientific or historical research, or statistical purposes — precisely because deleting event rows that underpin aggregate metrics corrupts the historical record without serving the data subject's interests. CWE-459 (Incomplete Cleanup) describes the other side: cleanup that removes more than necessary. Hard-deleting email event rows during erasure can silently corrupt dashboard metrics (open rates, click rates, send volume charts) by removing the denominator rows their calculations depend on, while the PII attached to those rows is what actually needed to go.
Why this severity: Low because aggregate corruption from over-deletion is a product reliability issue that becomes a compliance complication only when the corrupted metrics are relied on in regulatory or financial reporting.
compliance-consent-engine.data-subject-rights.analytics-integritySee full patternGDPR Article 7(1) requires you to demonstrate that consent was given — but demonstrating *how* and *by whom* a consent state was established requires an operational audit trail beyond the consent record itself. GDPR Article 30 requires records of processing activities. CWE-778 (Insufficient Logging) covers the broader risk: without logging the actor and source for every consent change, you cannot answer "who bulk-imported this contact without consent?" or "which admin override changed this contact's scope?" A consent record showing `granted = true` with no audit trail is legally weaker than one with a clear chain of custody. NIST AU-2 mandates logging of all events that could be relevant to regulatory compliance.
Why this severity: High because consent change logging gaps make it impossible to reconstruct the chain of custody for any consent state, undermining GDPR Art. 7(1)'s demonstrability requirement and exposing admin override and bulk import paths to undetectable misuse.
compliance-consent-engine.compliance-audit-trail.consent-change-loggingSee full patternGDPR Article 5(2) accountability requires that compliance records be trustworthy — a mutable audit log provides no assurance because any entry could have been silently modified after the fact. CWE-472 (External Control of Assumed-Immutable Web Parameter) and CWE-778 (Insufficient Logging) both apply when audit records can be retroactively altered. NIST AU-9 explicitly requires protection of audit information from unauthorized modification and deletion. An audit log that a rogue service account or a poorly-reviewed migration can `UPDATE` or `DELETE` is worthless as legal evidence — it cannot prove what actually happened, only what the log currently says.
Why this severity: High because a mutable audit log cannot be used as evidence in a regulatory investigation and provides no detection of tampering, invalidating GDPR Art. 5(2) accountability and ISO 27001:2022 A.8.15 compliance claims.
compliance-consent-engine.compliance-audit-trail.append-only-logSee full patternGDPR Article 30(1) requires records of processing activities to be maintained — without a defined retention window, these records will either grow unbounded (prompting an engineer to delete them without proper review) or get purged on an arbitrary schedule shorter than regulatory requirements. GDPR Article 5(2) accountability and CCPA §1798.185(a)(8) both require that records demonstrating compliance be available for regulatory inspection. Common guidance across CAN-SPAM, GDPR, and state privacy laws converges on 3-5 years for consent and processing records. A 90-day purge window — common in log management systems — falls catastrophically short of this.
Why this severity: Medium because incorrect retention periods create a gap between when records are purged and when a regulatory inquiry or litigation hold might require them, leaving the business unable to demonstrate compliance for historical processing activity.
compliance-consent-engine.compliance-audit-trail.log-retentionSee full patternGDPR Article 5(2) accountability and Article 30 records-of-processing requirements both assume that a controller can produce evidence of compliance on request — but "we have the data in the database" is not an evidence export mechanism. Without admin tooling that joins consent records, audit log entries, and erasure receipts into a coherent timeline, responding to a data protection authority inquiry or a data subject access request requires a bespoke database query every time, introducing inconsistency and delay. ISO 27001:2022 A.5.35 covers evidence of compliance activities. CCPA §1798.185(a)(8) requires businesses to document their data practices in a producible form.
Why this severity: Info because the gap affects operational efficiency and legal defensibility but does not itself cause a data subject's rights to be violated — compliance tooling is the means, not the obligation.
compliance-consent-engine.compliance-audit-trail.evidence-exportSee full patternGDPR Article 7(3) and CAN-SPAM §7704(a)(4) require opt-outs to be honored within a defined window — but "honored" means demonstrably processed end-to-end, not just acknowledged at the API boundary. CWE-778 (Insufficient Logging) applies directly: if the opt-out worker writes only a consent record but no audit log events, you cannot reconstruct when the job started, how long ESP suppression took, or whether the 10-business-day CAN-SPAM window was met for any specific request. When a data subject or regulator asks for the processing timeline of their opt-out, a consent record showing `granted = false` with no operational context is an incomplete answer.
Why this severity: Low because the opt-out itself is processed correctly — the logging gap creates a compliance demonstration problem rather than an active data subject rights violation, but that distinction disappears under a regulatory inquiry.
compliance-consent-engine.compliance-audit-trail.optout-events-loggedSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Compliance & Consent Engine Audit