Skip to documentation content

Consent records

The fields stored for every consent event, how explicit choices are distinguished from defaults, and what a signed receipt proves.

Every consent event your banner reports is written to a single row. This page lists what that row contains, because a record you cannot describe field by field is not evidence you can rely on.

What is stored

The banner posts to the consent endpoint, the server validates the payload and writes one row:

StoredNotes
Category decisionsnecessary, analytics, marketing, preferences — booleans; necessary is always true
Consent IDOpaque identifier generated in the browser and kept in the consent cookie
Client timestampThe time the browser reported, after clamping (below)
Server timestampWritten by the database on insert; this is the authoritative one
Banner and teamResolved server-side from the public banner ID in the payload
Event typeOne of consent_given, consent_changed, consent_default, accept_all, accept_necessary
Collection methodHow the state was collected — see the table below
Default-state flagTrue when the row was machine-written rather than chosen
Consent modeoptIn, optOut or notice, plus the mode actually resolved at runtime
Matched rule and geo sourceWhich regional rule matched, and how the location was determined
Country and regionCoarse location only, as provided by the edge
DomainThe domain the banner ran on
ReferrerRedacted to the banner's configured grouping level and truncated
Privacy signalgpc, dnt or gpc_dnt when one was present, plus a flag recording that it shaped the state
US purpose choicesSale/sharing, targeted advertising and sensitive data, when the visitor used the opt-out panel
Consent stringPopulated only when the framework producing it is switched on; see the note at the end of this page

Two ingestion paths, two sets of stored fields

Consent records reach the store through more than one endpoint, and they do not store the same fields. Read this before you describe CookieBeam's data handling in your own records of processing.

Banner and preference centreVersioned /api/v1/consent/log
IP addressNot storedNot stored
User agentNot storedNot stored
Page URLDomain onlyNot stored
ReferrerTruncated and redacted to the banner's grouping levelNot stored
Origin restricted to the banner's domainsYesNo; the caller authenticates with an API key instead
Category, collection method, default-state, privacy-signal fieldsWrittenNot written
SDK platform and versionNot writtenWritten when the caller sends them, as the mobile SDKs do

Neither path stores the visitor's IP address or user agent. The versioned endpoint is called server-to-server or from a native app, so the request's IP, user agent and referer describe the caller rather than the visitor, and it does not record them.

Records from the two paths are visibly different: only banner rows carry an event type and collection method, and only versioned-endpoint rows can carry an SDK platform.

Timestamp clamping

Browser clocks are wrong more often than people expect, and a skewed clock corrupts ordering. If the reported time is more than five minutes in the future or more than twenty-four hours in the past, the server replaces it with server time. The database's own insert timestamp is always authoritative regardless.

Explicit choices versus defaults

This is the distinction an auditor asks about first, so the schema carries it explicitly rather than leaving it to be inferred.

A row is machine-written when the runtime recorded a state the visitor never chose — an opt-out or notice model granting at page load, or an auto-grant in a region configured to hide the banner. Those rows are flagged, and the flag changes system behaviour:

  • No webhook is dispatched. A machine-written default must never tell your downstream systems "this visitor consented".
  • It is not billed as a consent log.
  • US purpose choices are forced to null, server-side. Those values exist only after a visitor actively saves the opt-out panel, so a default row cannot carry them.
  • It is not counted as a user action in banner metrics.

The collection method records the provenance:

ValueMeaning
bannerChosen in the consent banner
preference_centerSaved in the hosted preference centre — this is the value the preference-centre endpoint writes
preferencesDeclared by the runtime's own canonical state for a preference-centre change
apiSet through the browser API by your own code
default_optinOpt-in default that grants nothing — an audit row, recorded only under experiments
default_optoutGranted at load under an opt-out model
default_noticeGranted at load under a notice model
hidden_autograntGranted in a region configured not to show a banner

Two of those values describe the same event. The preference-centre endpoint writes preference_center, while the runtime's own canonical consent state uses preferences for a preference-centre change. Match on both if you are classifying records by provenance.

If your reporting treats every stored row as "a visitor consented", it will overstate consent for any region you run in opt-out or notice mode. Filter on the default-state flag.

When an explicit consent row is written and carries a consent ID, the server issues an opaque preference-centre link and returns it to the banner. The link is a random token, valid for thirty days, that resolves to that consent record — so a visitor can revisit their choice without you exposing internal banner or team identifiers.

Signed receipts

A receipt turns a stored row into a portable, verifiable artifact. Requesting one requires both the consent log ID and the matching consent ID, compared in constant time; the request must also come from a domain authorized for that banner.

The receipt is a signed token whose payload contains the receipt and consent log IDs, the public banner ID, a subject hash, the four category decisions, the US purpose choices, the event type and collection method, the privacy signal, the default-state flag, the recorded timestamp, the origin the receipt was issued for, and an evidence level. The subject hash is a SHA-256 hash of the consent ID — the receipt therefore carries a pseudonymous reference, not an email address or a name.

The evidence level is verified when the consent request carried the valid per-banner beacon token from your published script, and unverified when it did not (scripts published before the token existed, or records written before the field existed). Treat verified as a weak signal: the token ships inside your public script, so anyone who reads that script can send it. Receipts issued by the versioned API carry no evidence level: that endpoint is authenticated with your secret API key. Receipts you download from the dashboard or a CSV export also carry no evidence level; check the record's beacon token status in the log itself.

Verification is a separate public endpoint, and current receipts require you to pass the origin you expect.

What verification proves:

  • the token was signed with a CookieBeam signing key published at the verification-keys endpoint, and
  • its contents have not been altered since signing, and
  • it was issued for the origin you supplied.

What verification does not prove:

  • that a human made the choice — the consent endpoint is public, so a receipt attests that CookieBeam received and stored this record, not who or what sent it,
  • who the person behind the pseudonymous subject hash is,
  • that the banner was configured correctly, or that your categories describe your actual tags,
  • that the visitor saw, read, or understood the banner,
  • anything about a row that was machine-written — a receipt for a default row faithfully reports that it was a default, and the flag is inside the signed payload for exactly that reason.

A receipt is proof that this record was received, what was recorded, and that the recording is intact. It is not proof that the recording was lawful.

The record has a column for a transparency-and-consent-framework string. TCF support is built but not enabled in production — it is gated behind an environment flag that ships disabled by default, and the shipped configuration also has no vendor identifier set. The column therefore stays empty unless a deployment turns it on deliberately. Do not plan around it as an available feature, and do not describe your site as operating under that framework on the strength of this column existing.