---
title: "A consent architecture that survives an audit and a vendor migration"
summary: "A schema, a set of invariants, and the three queries every send path and every regulator eventually asks."
canonical: https://cpaas.co/en/learn/consent-architecture
kind: whitepaper
depth: advanced
origin: original
published: 2026-07-23T16:26:18.899Z
updated: 2026-10-14T16:26:19.543Z
topics: ["Compliance", "Architecture", "Omnichannel"]
language: en
---
# A consent architecture that survives an audit and a vendor migration

## Why this matters

Sooner or later someone asks a business to prove that a particular person agreed to a particular message: a regulator, a carrier investigating complaints, a court, or the customer. Most messaging stacks can say whether they may send *today*. Very few can say what they were allowed to do eighteen months ago, and why. This piece describes a design that can answer both, and that keeps working when you change messaging vendors. It assumes you already accept the idea in [consent is a data model](/en/learn/consent-is-a-data-model): consent is a ledger of events, not a single yes/no flag.

> **Interactive figure — consent-ledger.** The queries below are what this figure is doing underneath.
> Available at https://cpaas.co/en/learn/consent-architecture

## The event

One append-only table. Every row is an immutable fact that happened at a moment:

- **subject**: the contact identity, not the channel address. Phone numbers and email addresses change; the person does not.
- **channel**: sms, whatsapp, email, voice. Never "all".
- **purpose**: service, transactional, marketing, research. A closed list, versioned.
- **action**: granted, withdrawn, expired, disclosed.
- **at**: the time of the event itself, with time zone, not the time the row was inserted.
- **mechanism**: web form, keyword, verbal, imported, inferred.
- **evidence_ref**: a pointer to the artefact, such as a form submission id, a call recording id or an inbound message id.
- **actor**: who recorded it. A person, a system, an import job.
- **source_system**: where it originated, which matters enormously after an acquisition or a migration.

One row, appended, never updated. Everything the three queries need has to be on it, because reconstructing a missing field from application logs is the work this design exists to avoid.

```diagram:blocks
# What a single grant or withdrawal has to carry
Subject | The contact identity, not the channel address. Addresses change; the person does not.
Scope | Channel and purpose together. "Marketing on WhatsApp" is a grant. "Consent" is not.
Direction | Grant or withdrawal. A withdrawal is a new row with the opposite sign, never a deletion.
Mechanism | Web form, keyword, verbal, imported, inferred. Different evidentiary weight under scrutiny.
Evidence pointer | Submission id, recording id, inbound message id. The artefact itself lives elsewhere; the pointer lives here.
Timestamps | When the event happened and when the row was written. They differ during an import, and the difference matters.
> Six fields. A ledger missing any one of them can answer "may I send" but not "prove it", and the second question is the one that arrives with a lawyer.
```

## Flag or ledger: the difference in one table

| Question | A yes/no field on the contact | An event ledger |
|---|---|---|
| May I send this marketing message now? | Yes, if the field is current | Yes |
| What did we hold on 1 March last year? | No: the old value was overwritten | Yes: replay events up to that date |
| How was consent given, and where is the proof? | Rarely recorded | Mechanism and evidence pointer on every row |
| Can marketing be withdrawn while service messages continue? | Only with one field per purpose and channel | Built in: scope is part of each row |
| Does it survive a vendor migration? | Only if the vendor's export is complete | Yes: it lives in your systems |

## The invariants

**Nothing is ever updated or deleted.** A withdrawal is a new row. If your framework offers a "soft delete" here, resist it. The history is the asset. (Where a data subject's erasure request applies, handle it as a documented, separate process with legal advice, rather than as routine editing.)

**No row without an evidence reference.** "Imported from legacy system" is an acceptable evidence type if you record it honestly; a blank field is not.

**Purpose and channel are never empty.** A grant that does not say what it is for and where it applies cannot be enforced, and a system that stores it will over-apply it.

**Inferred consent is marked as inferred.** Some rows will come from behaviour rather than a statement. That can be legitimate under some rules and in some markets. Mixing it with explicit grants is not.

```diagram:checklist
# Four invariants the ledger never breaks
Nothing is updated or deleted | A withdrawal is a new row; erasure requests are a separate, documented process
No row without an evidence reference | "Imported from legacy system" is honest; a blank field is not
Purpose and channel are never empty | A grant that does not say what for and where will be over-applied
Inferred consent is marked as inferred | Never mixed with explicit grants
```

## The three queries

**May I send?** Given subject, channel, purpose and now: the latest event wins, expiry is applied, and the default is deny. This runs in the send path, so it needs an index and a cache with a short, bounded lifetime.

**What did we hold, then?** The same query at a past timestamp. This is the one an audit asks, and the one a flag-based model cannot answer.

**Show me everything for this person.** A data subject access request. If assembling it takes a week of engineering time, you do not have a consent architecture; you have logs.

```diagram:flow
# What the ledger is asked, in ascending order of urgency
May I send this? | Current state, one subject, one scope. Fast, in the send path, many times a second.
What does this person have? | Every scope for one subject, current state. The preference centre and the subject access request read this.
What did we hold, and when? | State at a past timestamp, with the evidence. Asked by a regulator, an auditor, or a lawyer.
> The third query is why the ledger is append-only. It cannot be answered by a table of current values, no matter how well indexed.
```

## Where it lives

**In your systems, not a vendor's.** You are accountable for it, you must produce it after switching platforms, and retention obligations outlast contracts. A consent record that only exists in a console you may cancel is a liability with a renewal date.

Not in the CRM, not in the marketing platform, not in a vendor's contact list either. All three are legitimate *readers* of the ledger; none is a good home for it, because the ledger has to outlive each of them and answer questions about periods when a different system was in place.

Push the derived state to vendors (suppression lists, preference syncs) but keep the ledger. In practice that means a small internal service that other systems call, which is more work than a column on the contact record, and far less work than reconstructing history later.

```diagram:path
# The ledger stays home; derived state travels
The consent ledger | A small internal service, in your systems | Derived state is pushed out
Suppression lists and preference syncs | The copies vendors receive | Read by
CRM, marketing platform, messaging vendor | Legitimate readers — none of them a home for the ledger
> The ledger has to outlive each reader and answer for periods when a different system was in place.
```

## Disclosure belongs here too

The EU AI Act's transparency obligations, which apply from 2 August 2026, create a fact of the same shape: this person, on this channel, at this time, was told they were interacting with an AI system. See [Article 50 for messaging teams](/en/learn/ai-act-article-50-for-messaging).

Same table, with an action of `disclosed`. The alternative, a second system with its own timestamps and its own gaps, is how organisations end up unable to answer a simple question about their own behaviour.

## The migration test

The honest way to check this design: assume you replace your messaging vendor next quarter. Can you export the ledger, import it elsewhere, and answer all three queries on day one?

If yes, the architecture is sound. If no, you have built a dependency you did not intend to buy.

```diagram:decision
# You replace your messaging vendor next quarter
Can you export the ledger, import it elsewhere, and answer all three queries on day one? | The architecture is sound | You have built a dependency you did not intend to buy
```

## The trade-offs

This design is not free. An append-only table grows forever, so you need archiving and a retention policy agreed with legal counsel. The "may I send?" query must be fast, which means caching, and caches can be briefly stale after a withdrawal; keep their lifetime short and treat withdrawals as urgent. And teams used to editing a contact record will need to learn to append instead.

## What to take away

One append-only table, six essential fields, a few invariants and three queries. It is a modest piece of work, and it is the only part of a messaging stack that answers to a regulator rather than to a vendor. Build it before the first send, because retrofitting it means reconstructing history you no longer have.

## Where to go next

- [The gap nobody fills: service-to-service identity](/en/learn/service-to-service-identity): how systems prove they are acting for a specific customer when they read or write this ledger.
- [Federating systems without merging them](/en/learn/federating-without-merging): why "one source of truth per fact" matters when several systems share contacts.

## Sources

[^1]: [Regulation (EU) 2016/679 (General Data Protection Regulation)](https://eur-lex.europa.eu/eli/reg/2016/679/oj) — EUR-Lex
[^2]: [Guidelines 05/2020 on consent under Regulation 2016/679](https://www.edpb.europa.eu/our-work-tools/our-documents/guidelines/guidelines-052020-consent-under-regulation-2016679_en) — European Data Protection Board
[^3]: [Article 50: Transparency obligations for providers and deployers of certain AI systems](https://ai-act-service-desk.ec.europa.eu/en/ai-act/article-50) — European Commission, AI Act Service Desk
[^4]: [Commission starts enforcing AI Act rules and new transparency requirements on 2 August](https://digital-strategy.ec.europa.eu/en/news/commission-starts-enforcing-ai-act-rules-and-new-transparency-requirements-2-august) — European Commission, 31 July 2026

---

Published by the company behind five products in this category. Those products appear in this site's directory alongside competitors under the same published criteria and are labelled as its own. Editorial content does not recommend them.
