Datafly.js SDKConsent Management

Consent Management

Datafly.js includes built-in consent management that integrates with popular Consent Management Platforms (CMPs). Consent state is read from the page automatically and used to gate both event collection (client-side) and event delivery (server-side).

Consent is enforced at two levels:

  1. Client-side (Datafly.js) — reads consent state from the CMP and includes it with every event. Client-side tag injection is gated by consent category.
  2. Server-side (Signals) — re-checks consent before delivering events to each vendor. Even if an event reaches the server, it will not be forwarded to a vendor unless the required consent category is granted.

This dual enforcement ensures that vendor data delivery respects user consent regardless of client-side manipulation.

User grants consent
       │
       ▼
┌──────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  Datafly.js  │────>│ Signal endpoint  │────>│ Vendor delivery │
│  reads CMP   │     │  stores consent  │     │  re-checks      │
│  includes in │     │  with event      │     │  consent before │
│  event       │     │                  │     │  delivery       │
└──────────────┘     └──────────────────┘     └─────────────────┘

Datafly carries two views of consent on every event:

  • categories — keyed by your CMP’s codes verbatim (Cookiebot’s statistics, OneTrust’s C0002, custom codes). Used for CMP-specific blueprints and audit.
  • canonical — keyed by Signal’s CMP-agnostic vocabulary. Vendor blueprints (GA4, Meta, Google Ads, TikTok …) reference this so they stay portable.

The canonical vocabulary:

CanonicalDescriptionExample vendors
essentialStrictly necessary — always grantedFirst-party identity cookie
functionalPreferences and UI personalisationA/B testing tools, chat widgets
analyticsPerformance and aggregate measurementGoogle Analytics 4, BigQuery
marketingAdvertising and conversion APIsMeta CAPI, Google Ads, TikTok, Pinterest
personalizationCross-property profilingRecommender services

The customer maps their CMP’s category codes onto this vocabulary in Settings → Consent. See Canonical Categories for the full guide.

The first-party identity cookie (_dfid) is always set so events within a visit can be correlated, but when the pipeline has a consent provider its lifetime follows consent. It is a session cookie until a category that allows persistence is granted, and then becomes a persistent cookie with the pipeline’s cookie lifetime (400 days unless set shorter). Choose those categories in the Collector tab under Persist identifiers when consented to (essential always counts). If none are chosen, persistence follows your active consent categories, or analytics or marketing consent. The cookie carries only an anonymous identifier.

Supported CMPs

Datafly.js automatically detects and reads consent from the following CMPs. No additional configuration is required — just install your CMP as usual and Datafly.js will find it.

OneTrust

Datafly.js reads the OptanonConsent cookie and parses the consent groups:

OneTrust groupDatafly category
C0001 (Strictly Necessary)Always allowed
C0002 (Performance)analytics
C0003 (Functional)functional
C0004 (Targeting)marketing
OptanonConsent cookie value:
groups=C0001%3A1%2CC0002%3A1%2CC0003%3A0%2CC0004%3A0

Parsed as:
  C0001: 1 (Necessary - always on)
  C0002: 1 (Analytics - granted)
  C0003: 0 (Functional - denied)
  C0004: 0 (Marketing - denied)

Cookiebot

Datafly.js reads the CookieConsent cookie and maps the Cookiebot fields:

Cookiebot fieldDatafly category
necessaryAlways allowed
statisticsanalytics
preferencesfunctional
marketingmarketing

If you manage consent yourself or use a CMP not listed above, Datafly.js reads a _df_consent cookie with a simple key-value format:

_df_consent=analytics=1;marketing=0;functional=1

Values of 1 or true indicate consent granted. Values of 0 or false indicate consent denied.

Configuration

Configure the default consent state (before the user makes a choice) in your source configuration:

{
  "consent": {
    "mode": "onetrust",
    "default_state": {
      "analytics": false,
      "marketing": false,
      "functional": false
    }
  }
}

The mode option tells Datafly.js which CMP to look for first. Supported values:

ModeCMPCookie read
"onetrust"OneTrustOptanonConsent
"cookiebot"CookiebotCookieConsent
"custom"Your own CMP_df_consent
"auto" (default)Auto-detectChecks all in order

Opt-in vs opt-out regions

For regions where consent is not required (e.g., United States under current law), you can set defaults to true:

{
  "consent": {
    "mode": "auto",
    "default_state": {
      "analytics": true,
      "marketing": true,
      "functional": true
    }
  }
}
⚠️

Consent defaults should match your legal obligations. Setting defaults to true means events will be collected and delivered before the user interacts with a consent banner. Consult your legal team for the correct default state for each region you operate in.

If your CMP is not auto-detected, set the _df_consent cookie from your consent handler:

// Example: integrating with a custom consent banner
consentBanner.onAccept((categories) => {
  const parts = [];
  if (categories.analytics) parts.push('analytics=1');
  if (categories.marketing) parts.push('marketing=1');
  if (categories.functional) parts.push('functional=1');
 
  document.cookie = `_df_consent=${parts.join(';')};path=/;max-age=31536000`;
});
 
consentBanner.onReject(() => {
  document.cookie = '_df_consent=analytics=0;marketing=0;functional=0;path=/;max-age=31536000';
});

You can read the current consent state from Datafly.js at any time:

const consent = datafly.getConsent();
console.log(consent);
// { analytics: true, marketing: false, functional: true }

This is useful for conditionally showing UI elements or gating your own application features based on consent.

If you use Datafly’s client-side tag injection (for tags that must run in the browser), tags are consent-gated by category. A tag will only be injected if the user has granted consent for its category:

datafly.injectTag({
  id: 'hotjar',
  src: 'https://static.hotjar.com/c/hotjar-123456.js',
  consentCategory: 'analytics',
  timing: 'post-render',
});
// This tag will only load if consent.analytics === true

Consent is re-checked at the time of injection. If a user revokes consent, subsequently scheduled tags will not be injected.

Calls Datafly.js makes to other companies

A few features make Datafly.js call a third party directly from the browser. Each call waits for the visitor’s consent to its own consent category, checked at the moment it would fire:

CallConsent category
GA4 session beacon (session_start to /g/collect), and creating or reading the GA client IDThe GA4 row’s Consent category in Identity → ID Syncs. If it is left at the default, the GA4 integration’s category (when all your published GA4 integrations share one), else analytics.
ID syncs, both the init-time syncs and the syncs the collection server asks forEach vendor’s Consent category in Identity → ID Syncs; marketing by default.
Floodlight match pixel and vendor match pixels (Meta, Pinterest, LinkedIn, …)The vendor’s Consent category in Identity → ID Syncs; marketing by default.
Cross-domain identity hubThe consent that lets the _dfid cookie persist.

A category is one of your banner’s category codes, or a canonical name (analytics, marketing, …) when none of your codes has that name. Signal applies the visitor’s stored choice for it, or, before they have chosen, the Before the visitor chooses setting for that category (see Consent). This is the same decision the server makes for delivery.

These settings take effect without a new tag. Datafly.js asks the collection server for your pipeline’s current consent rules once a session, and again every 15 minutes, and holds these calls until the first answer arrives, usually a fraction of a second after the first event. Your events, including engagement heartbeats, session_start and first_visit, go only to your collection endpoint, and the server checks each destination’s own consent category when it delivers them.

UK first-party analytics. If your banner keeps a first-party analytics category on after “Reject all” (the UK DUAA exemption), key your GA4 integration to it, and set it to Allowed before the visitor chooses if your policy lets it run until they object. Classify it as analytics, not essential: essential should mean a category the visitor cannot switch off.

With no consent provider configured on the pipeline, Datafly.js has no consent signal to read and does not hold these calls back. If you manage consent outside Signal, load the tag only after the visitor consents.

Call datafly.refreshConsent() from your CMP’s change callback (for example OneTrust OptanonWrapper or Cookiebot CookiebotOnAccept). Datafly.js also notices a new choice your page has saved without that call, before the next event it sends.

  • Consent granted: the calls it allows start straight away. The GA4 beacon still fires at most once per session.
  • Consent withdrawn: they stop, and the stored GA client ID is deleted, so events no longer carry it. A new ID is created if analytics consent is granted again.

The current consent state is sent with every event, allowing the server to make delivery decisions:

{
  "type": "track",
  "event": "Product Viewed",
  "context": {
    "consent": {
      "analytics": true,
      "marketing": false,
      "functional": true
    }
  }
}

The server uses this to determine which vendors can receive the event. For example, if marketing is false, the event will not be delivered to Meta, TikTok, or other marketing vendors, even though it was collected.

With a consent provider configured, Datafly.js also sends chosen: the categories your banner has actually stored for the visitor. It is empty before they make a choice. The server applies your per-category Before the visitor chooses settings to every category not in chosen (see Consent), so you can change those settings without a new tag.

Detection order

When mode is "auto" (the default), Datafly.js checks for CMP cookies in this order:

  1. OptanonConsent (OneTrust)
  2. CookieConsent (Cookiebot)
  3. _df_consent (custom)
  4. Default state (all false unless overridden)

The first match wins. If you have multiple CMPs on the page (not recommended), the first detected CMP takes precedence.