> For the complete documentation index, see [llms.txt](https://docs.chord.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.chord.co/collect-data/everyone-else/browser-events/consent.md).

# Consent

Chord enforces user consent before forwarding events to destinations. When a destination has consent requirements configured, no event is sent to that destination — and no destination script is loaded in the browser — until the user has granted the required consent categories.

This page explains how to connect your consent management platform (CMP), how consent categories work, and how they control delivery to each destination. On Shopify, see [Shopify: Consent](/collect-data/shopify/browser-events/consent.md) instead.

## The consent lifecycle in brief

What happens, in order, on a visit to a storefront with a CMP configured:

1. **Before the CMP publishes a consent state, events are held in a browser queue and nothing reaches Chord.** This covers the time the CMP script takes to load — not the time the visitor spends reading the banner.
2. **Once the CMP publishes a state, the queue flushes.** That first state is the jurisdiction default the CMP is configured with: typically permissive under an opt-out model (common in the US) and denied-except-strictly-necessary under an opt-in model (common in the UK and EU).
3. **From that point on, every event is sent to Chord — whether consent was granted or denied.** A denial is recorded on the event; it does not stop collection. Enforcement happens per destination, not in the browser's decision to send.
4. **Consent is read fresh and stamped immediately before each send**, including when a queued event is replayed. A queued event can therefore carry the visitor's actual choice rather than the default it was queued under.
5. **When the visitor changes their choice, the new preferences apply to every later event**, and the analytics package fires a `Consent Preferences Updated` event. Server destinations receive that event whatever the visitor's consent, so a platform can act on a revocation. Browser destinations never receive it.
6. **Chord decides delivery per destination.** A destination receives an event only if every category in its **Consent Requirements** is `true`. A destination with an empty requirements list receives everything.
7. **Nothing is retroactive.** Events already delivered under a permissive default stay delivered.
8. **All of the above requires the `consent` option to be set** when the analytics package is initialized. Without it there is no queue and no consent stamp — events are collected and forwarded with no consent data at all.

### What a full denial does — and doesn't — stop

If a visitor declines every category in the CMP banner, **the browser still sends every event to Chord.** Collection does not stop. Each event carries `categoryPreferences` with every category set to `false`, and Chord records it like any other event.

What stops is delivery:

* A destination with one or more **Consent Requirements** receives nothing. In device mode, its third-party script is never loaded.
* A destination with an **empty** Consent Requirements list still receives every event. A full denial does not gate it — configuring the requirements list is what makes consent enforceable for that destination.
* `Consent Preferences Updated` still reaches server destinations, so a platform can act on the revocation. Browser destinations don't receive it.

If you need collection itself to stop, your storefront has to stop calling the analytics package. No setting in Chord turns a denial into non-collection.

## Consent categories

Consent categories are the units of permission that users grant or deny. Before configuring consent requirements on a destination, you must define your categories in Chord under **CDP > Consent Categories**.

Each category has:

* **Name** — human-readable label shown in Chord
* **External ID** — the category identifier used by your consent management platform (CMP). Must match exactly what your CMP emits.
* **Description** — optional explanation of what data collection this category covers
* **Active** — whether this category is currently enforced

The **External ID** is the field that connects Chord categories to your CMP. For example, OneTrust uses category codes like `C0002` for analytics cookies — the External ID for your analytics category must be set to `C0002` for the mapping to work.

## Consent management platform (CMP) integration

The analytics package bridges your CMP and Chord. It reads the user's current consent state from the CMP, stamps each outgoing event with the user's category preferences, and updates those stamps dynamically as consent changes.

### Supported CMPs

* **OneTrust** — detected via `window.OneTrust`; categories read from `window.OnetrustActiveGroups`
* **Cookiebot** — detected via `window.Cookiebot`
* **Ketch** — detected via `window.ketch` and `window.ketchConsent`
* **Shopify** — detected via `window.Shopify.customerPrivacy`
* **Segment** — detected via `window.segmentConsent`

The CMP integration is configured when you initialize the analytics package:

```javascript
const chord = new ChordAnalytics({
  consent: 'onetrust', // 'cookiebot' | 'ketch' | 'onetrust' | 'shopify' | 'segment'
  cdpDomain: process.env.CHORD_DOMAIN,
  cdpWriteKey: process.env.CHORD_WRITE_KEY,
  // rest of your configuration
})
```

The keys each CMP reports, which become your category External IDs:

* `onetrust`: OneTrust group IDs, such as `C0001`, `C0002`, `C0003`, plus any custom group IDs
* `cookiebot`: `necessary`, `preferences`, `statistics`, `marketing`
* `ketch`: your Ketch purpose keys
* `segment`: your `customPreferences` keys merged with your `destinationPreferences` keys

If your CMP isn't listed, contact Chord to discuss adding it.

If the `consent` option is not set, the analytics package does not read your CMP at all. Events are still collected and forwarded, but they carry no consent information — see **Events with no consent data** below.

### Consent readiness and event queueing

When a CMP is configured, the analytics package checks whether that CMP has published a consent state before sending each event. If it has not, **the event is held in a queue** rather than being dropped. The analytics package polls every 100 ms and flushes the queue as soon as the state is available.

This queue covers the short window while the CMP script is still loading — not the period before the visitor answers the banner. Most CMPs publish a default state as soon as they load, so events typically resume immediately, stamped with those defaults rather than with the visitor's eventual choice. OneTrust, for example, is considered ready once `window.OnetrustActiveGroups` is populated, which happens before any banner interaction.

The queue is held in memory and is not capped. It is also mirrored to `sessionStorage` so it survives navigation within the session; that mirror keeps only the most recent 50 events and is skipped entirely if it would exceed 100 KB. There is no time-based expiry — queued events are delivered whenever the CMP state becomes available.

### Consent stamping

On every event, the analytics package fetches the user's current category preferences directly from the CMP (no caching). The result is added to the event under `context.consent.categoryPreferences`:

```json
{
  "context": {
    "consent": {
      "categoryPreferences": {
        "C0001": true,
        "C0002": true,
        "C0003": false
      }
    }
  }
}
```

The keys in `categoryPreferences` are the **External IDs** of your consent categories. Values are `true` (granted) or `false` (denied).

Note that denying consent does not stop the analytics package from sending the event. The event is still collected, stamped with the denial, and forwarded to Chord, where consent requirements decide which destinations receive it. This preserves a record of the decision and lets enforcement stay configurable.

## Configuring consent requirements on a destination

Open the destination's configuration in Chord and add one or more consent categories to the **Consent Requirements** list. Each required category must resolve to `true` in the event's `categoryPreferences` for the event to be forwarded.

A destination with an empty **Consent Requirements** list receives every event that passes host and event filters, whatever the user's consent preferences say. Stamping consent on an event does nothing by itself — the categories have to be configured here as well.

### Requiring all consent categories

Consent requirements use **AND logic**: if you specify two categories, both must be granted. There is no OR operator — if you want a destination to fire when any one of several categories is granted, create separate destination configurations.

### Events with no consent data

If an event arrives with no `categoryPreferences` at all, every destination treats it as consented and sends it on, even a destination with consent requirements.

Events arrive with no consent data in two common situations:

* A storefront that hasn't set the `consent` option in the analytics package.
* Server events sent from your backend. Consent is only known in the browser, so a server event carries no consent signal.

This keeps transactional events, such as Order Completed and Order Refunded, flowing to every destination. To control what browser events send, set the `consent` option so every browser event carries the shopper's choices.

## How consent gates event delivery

### Device mode

Device mode destinations run as browser plugins — the destination's third-party script (e.g., a pixel or SDK) loads in the visitor's browser, and events are sent directly from the browser to the destination's servers.

Before a device-mode destination handles an event, the analytics package checks the destination's consent requirements against the event's `categoryPreferences`. If any required category isn't granted, the destination never sees the event.

**Third-party scripts are loaded lazily.** A device mode destination's script is only loaded after the first event passes all filters, including consent. If the user never grants the required consent, the destination script is never injected into the page.

### Cloud mode

Cloud mode destinations receive events server-side via Chord's pipeline. The same check runs in Chord before the event is sent to the destination. Events that don't meet the consent requirements are dropped.

## The `Consent Preferences Updated` event

When a user changes their consent choices, the analytics package fires a `Consent Preferences Updated` event. Server destinations **always receive it**, whatever the visitor's category preferences. Browser destinations never receive it, so a visitor who declined never loads a pixel.

This allows server destinations to register the updated preferences — for example, to update suppression lists or unsubscribe records in a marketing platform — even when the user has revoked consent for that destination's category.

## Example: SFMC Collect.js

Salesforce Marketing Cloud Collect.js is a device mode destination. If it is configured with a consent requirement of `C0002` (analytics cookies):

1. A visitor arrives in a region where the CMP's default denies analytics cookies. Once the CMP loads, events are stamped with that default, `categoryPreferences: { "C0002": false }`, so nothing is sent to SFMC and the Collect.js script isn't loaded. (Events only arrive with no `categoryPreferences` if the `consent` option isn't set, and SFMC then gets them.)
2. The visitor dismisses the banner and denies analytics cookies — `categoryPreferences: { "C0002": false }`. All subsequent events for SFMC are dropped. The Collect.js script is not loaded if it hasn't been already.
3. The visitor accepts analytics cookies — `categoryPreferences: { "C0002": true }`. Events resume flowing to SFMC. If the Collect.js script was never loaded, it is loaded now on the next passing event.
4. The visitor later revokes consent. A `Consent Preferences Updated` event is fired, but SFMC Collect.js is a browser destination, so it doesn't receive it. All later track and page events are blocked again.

## Troubleshooting

**Events are not reaching a destination even though consent was granted.** Check that the External IDs configured on your consent categories in Chord exactly match the category codes your CMP emits. A mismatch (e.g., `c0002` vs `C0002`) causes every event to fail the consent check.

**Events are flowing to a destination even though the user denied consent.** Verify that the destination has consent requirements configured. A destination with no consent requirements set will receive all events that pass host and event filters, regardless of the user's consent preferences.

**Events arrive with no consent object at all.** Confirm that the `consent` option is set when the analytics package is initialized. Without it, the analytics package never reads your CMP, and every event is treated as having no consent data.

**The destination script loads before consent is given.** This indicates that at least one event is passing the consent filter before consent is granted — most likely because `categoryPreferences` is absent, so the event is treated as consented. If you need explicit opt-in before the script loads, set the `consent` option.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.chord.co/collect-data/everyone-else/browser-events/consent.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
