Cookie Consent Opt-In Rate by Category
Also known as: Consent Opt-In Rate, Cookie Acceptance Rate by Category, Cookie Opt-In Rate, CMP Opt-In Rate, Privacy Preference Opt-In Rate
Measures, for each optional cookie category (analytics, functional, personalization, advertising), the share of users whose latest explicit consent decision in the reporting period is granted, out of all users who recorded a decision for that category. Financial services marketing and privacy teams use it to size the measurable and addressable digital audience, diagnose the impact of consent banner changes, and compare consent outcomes across opt-in jurisdictions governed by the GDPR and the ePrivacy Directive.
Business Use Case
1️⃣ Digital Marketing: when the advertising-category opt-in rate for EEA users drops by more than 5 percentage points week over week after a banner or preference-center release, roll back to the previous approved consent_version and open a review with the Privacy Office before relaunching. 2️⃣ Marketing Analytics: apply the analytics-category opt-in rate by country as the coverage factor in monthly paid media reporting, so campaign conversions for account-opening and loan-application journeys are scaled and footnoted for unconsented traffic rather than read as true volume changes. 3️⃣ Digital Product: before each consent_version release, A/B test banner layouts that give Accept and Reject equal prominence and ship only the variant that improves opt-in without reducing reject-button visibility, with sign-off from the Privacy Office. 4️⃣ Personalization & CRM Marketing: set market-level rollout gates for on-site personalized offers (credit card, savings and investment product recommendations), launching only where the personalization-category opt-in rate yields the minimum audience size required for a readable test.
Business Criticality
High
Journey / Navigation Area
Generic
Owning Function
Marketing
Scope
User
Formula
Calculation Notes
1️⃣ Unit of analysis is the user × consent_category pair, where the user key is the CMP consent_id (first-party, strictly necessary cookie written by the consent platform) with user_pseudo_id (GA4) or ECID (AEP) as fallback. Keep only the latest explicit decision (ordered by event timestamp) per user key per category inside the reporting window. Numerator = pairs whose latest status is granted; denominator = all pairs with a granted or denied decision. Only these values count: content_type consent_banner | preference_center; consent_method banner | preference_center; consent_action accept_all | reject_all | save_preferences | withdraw; consent_status granted | denied. Any other interaction (banner close, dismissal, no interaction) sits outside both terms and belongs to the banner interaction rate. 2️⃣ Compute every category independently and never blend categories into a single rate. A banner accept_all click writes one granted decision per optional category, reject_all writes one denied decision per category, save_preferences writes the individual preference-center toggles, and withdraw writes a denied decision for the withdrawn category. 3️⃣ Exclude strictly necessary and security storage, which is not subject to opt-in, and never push CMP default states applied before user interaction (including pre-set granted defaults in opt-out jurisdictions) as select_content decisions; only explicit user actions from the allow-list count. 4️⃣ Denied users must stay in the denominator. Keying on consent_id keeps them countable: in Consent Mode advanced, post-deny cookieless pings arrive with a null user_pseudo_id, and in AEP the Web SDK drops events once consent is out, so the decision event must be sent before the out state is applied. Consent Mode basic sends nothing after a deny, so in that setup source the rate from the CMP consent log, and in every setup reconcile daily decision totals between the analytics export and the CMP consent log before publishing the rate. 5️⃣ A withdrawal or change later in the window overrides the earlier decision for the same consent_id. Cookie clearing, private browsing and new devices generate new consent_id values and new prompts, so cross-period trends are read at user-key level, not person level; when comparing banner variants, restrict to decisions made on the same consent_version. 6️⃣ Exclude internal and employee traffic (GA4 traffic_type = internal; AEP _tenant.trafficType = internal), QA and staging hostnames, known bots (excluded automatically in GA4; user-agent pattern filter in AEP) and duplicate CMP callbacks (same user key, category and status within 5 seconds). Always break out by country or regulatory region, because opt-in regimes (GDPR/ePrivacy) and opt-out regimes (US state privacy laws such as CCPA/CPRA) use different default states and are not comparable in a blended figure. 7️⃣ Decision-level variant (diagnostic only): granted decision events ÷ all granted or denied decision events per category, which counts re-consents and changes of mind; report it alongside, never in place of, the user-level rate.
Technical Details
Events
| Platform | Event |
|---|---|
| Google Analytics 4 | select_content |
| Adobe | select_content |
Data Mappings
W3 Data Layer
{
"digitalData": {
"pageInstanceID": "savings-accounts-compare-rates-production",
"page": {
"pageInfo": {
"pageID": "savings-accounts-compare-rates",
"pageName": "Savings Accounts | Compare Rates",
"destinationURL": "https://www.examplebank.com/de/savings-accounts",
"referringURL": "https://www.examplebank.com/de/",
"sysEnv": "desktop",
"language": "de-DE",
"geoRegion": "DE"
},
"category": {
"primaryCategory": "savings",
"pageType": "product"
}
},
"user": [
{
"profile": [
{
"profileInfo": {
"returningStatus": "returning",
"language": "de-DE"
}
}
]
}
],
"privacy": {
"accessCategories": [
{
"categoryName": "strictly_necessary",
"domains": [
"www.examplebank.com"
]
},
{
"categoryName": "analytics",
"domains": [
"www.examplebank.com",
"region1.google-analytics.com"
]
},
{
"categoryName": "functional",
"domains": [
"www.examplebank.com"
]
}
]
},
"event": [
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "accept_all",
"type": "consent",
"timeStamp": "2026-03-17T09:40:05.482Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "consent_banner"
},
"attributes": {
"content_type": "consent_banner",
"content_id": "consent_banner_accept_all",
"consent_category": "analytics",
"consent_status": "granted",
"consent_action": "accept_all",
"consent_method": "banner",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-3b17e9d0",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "accept_all",
"type": "consent",
"timeStamp": "2026-03-17T09:40:05.482Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "consent_banner"
},
"attributes": {
"content_type": "consent_banner",
"content_id": "consent_banner_accept_all",
"consent_category": "functional",
"consent_status": "granted",
"consent_action": "accept_all",
"consent_method": "banner",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-3b17e9d0",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "accept_all",
"type": "consent",
"timeStamp": "2026-03-17T09:40:05.482Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "consent_banner"
},
"attributes": {
"content_type": "consent_banner",
"content_id": "consent_banner_accept_all",
"consent_category": "personalization",
"consent_status": "granted",
"consent_action": "accept_all",
"consent_method": "banner",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-3b17e9d0",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "accept_all",
"type": "consent",
"timeStamp": "2026-03-17T09:40:05.482Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "consent_banner"
},
"attributes": {
"content_type": "consent_banner",
"content_id": "consent_banner_accept_all",
"consent_category": "advertising",
"consent_status": "granted",
"consent_action": "accept_all",
"consent_method": "banner",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-3b17e9d0",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "save_preferences",
"type": "consent",
"timeStamp": "2026-03-17T09:42:18.215Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "preference_center"
},
"attributes": {
"content_type": "preference_center",
"content_id": "consent_preferences_save",
"consent_category": "analytics",
"consent_status": "granted",
"consent_action": "save_preferences",
"consent_method": "preference_center",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-8f41c2a7",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "save_preferences",
"type": "consent",
"timeStamp": "2026-03-17T09:42:18.215Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "preference_center"
},
"attributes": {
"content_type": "preference_center",
"content_id": "consent_preferences_save",
"consent_category": "functional",
"consent_status": "granted",
"consent_action": "save_preferences",
"consent_method": "preference_center",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-8f41c2a7",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "save_preferences",
"type": "consent",
"timeStamp": "2026-03-17T09:42:18.215Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "preference_center"
},
"attributes": {
"content_type": "preference_center",
"content_id": "consent_preferences_save",
"consent_category": "personalization",
"consent_status": "denied",
"consent_action": "save_preferences",
"consent_method": "preference_center",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-8f41c2a7",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
},
{
"eventInfo": {
"eventName": "select_content",
"eventAction": "save_preferences",
"type": "consent",
"timeStamp": "2026-03-17T09:42:18.215Z"
},
"category": {
"primaryCategory": "consent",
"subCategory1": "preference_center"
},
"attributes": {
"content_type": "preference_center",
"content_id": "consent_preferences_save",
"consent_category": "advertising",
"consent_status": "denied",
"consent_action": "save_preferences",
"consent_method": "preference_center",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-8f41c2a7",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35"
}
}
],
"version": "1.0"
}
}GA4 Data Layer
{
"event": "select_content",
"content_type": "preference_center",
"content_id": "consent_preferences_save",
"consent_category": "analytics",
"consent_status": "granted",
"consent_action": "save_preferences",
"consent_method": "preference_center",
"consent_version": "2026.03",
"consent_region": "EEA",
"consent_decision_id": "cd-20260317-8f41c2a7",
"consent_id": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35",
"page_type": "product"
}Adobe Client Data Layer
{
"event": "select_content",
"eventInfo": {
"contentType": "preference_center",
"contentId": "consent_preferences_save",
"consent": {
"category": "analytics",
"status": "granted",
"action": "save_preferences",
"method": "preference_center",
"version": "2026.03",
"region": "EEA",
"decisionId": "cd-20260317-8f41c2a7",
"consentId": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35",
"time": "2026-03-17T09:42:18.215Z"
}
},
"page": {
"name": "Savings Accounts | Compare Rates",
"type": "product",
"siteSection": "savings",
"url": "https://www.examplebank.com/de/savings-accounts",
"language": "de-DE"
},
"privacy": {
"consentState": {
"analytics": "granted",
"functional": "granted",
"personalization": "denied",
"advertising": "denied"
}
}
}XDM Mapping
{
"eventType": "web.webinteraction.linkClicks",
"timestamp": "2026-03-17T09:42:18.215Z",
"identityMap": {
"ECID": [
{
"id": "48291057364829105736482910573648291057",
"authenticatedState": "ambiguous",
"primary": true
}
]
},
"web": {
"webInteraction": {
"name": "select_content",
"type": "other",
"linkClicks": {
"value": 1
}
},
"webPageDetails": {
"name": "Savings Accounts | Compare Rates",
"URL": "https://www.examplebank.com/de/savings-accounts",
"siteSection": "savings"
}
},
"placeContext": {
"geo": {
"countryCode": "DE"
}
},
"consents": {
"collect": {
"val": "y"
},
"share": {
"val": "n"
},
"personalize": {
"content": {
"val": "n"
}
},
"marketing": {
"preferred": "unknown",
"any": {
"val": "n"
}
},
"metadata": {
"time": "2026-03-17T09:42:18.215Z"
}
},
"_tenant": {
"consent": {
"category": "analytics",
"status": "granted",
"action": "save_preferences",
"method": "preference_center",
"version": "2026.03",
"region": "EEA",
"decisionId": "cd-20260317-8f41c2a7",
"consentId": "cmp-7d2e4a91-5c3b-4f08-9e61-2a8b0c4d7f35",
"contentType": "preference_center"
},
"trafficType": "external"
}
}SQL Queries
Google Analytics 4 (BigQuery)
-- ==========================================
-- 1. GOOGLE BIGQUERY SQL
-- ==========================================
WITH consent_events AS (
SELECT
COALESCE((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_id'), user_pseudo_id) AS user_key,
event_timestamp,
COALESCE(geo.country, 'unknown') AS country,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'content_type') AS content_type,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_category') AS consent_category,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_status') AS consent_status,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_action') AS consent_action,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_method') AS consent_method,
(SELECT COALESCE(value.string_value, CAST(value.int_value AS STRING)) FROM UNNEST(event_params) WHERE key = 'consent_version') AS consent_version,
COALESCE((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_region'), 'unknown') AS consent_region,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'consent_decision_id') AS consent_decision_id
FROM `your-project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260301' AND '20260331'
AND event_name = 'select_content'
AND COALESCE((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'traffic_type'), '') <> 'internal'
AND COALESCE(device.web_info.hostname, '') NOT LIKE '%staging%'
AND COALESCE(device.web_info.hostname, '') NOT LIKE 'qa.%'
),
explicit_decisions AS (
SELECT
*,
LAG(consent_status) OVER (PARTITION BY user_key, consent_category ORDER BY event_timestamp) AS prev_status,
LAG(event_timestamp) OVER (PARTITION BY user_key, consent_category ORDER BY event_timestamp) AS prev_event_timestamp
FROM consent_events
WHERE user_key IS NOT NULL
AND content_type IN ('consent_banner', 'preference_center')
AND consent_method IN ('banner', 'preference_center')
AND consent_action IN ('accept_all', 'reject_all', 'save_preferences', 'withdraw')
AND consent_category IN ('analytics', 'functional', 'personalization', 'advertising')
AND consent_status IN ('granted', 'denied')
),
deduplicated_decisions AS (
SELECT *
FROM explicit_decisions
WHERE NOT (
prev_status IS NOT NULL
AND prev_status = consent_status
AND event_timestamp - prev_event_timestamp < 5000000
)
),
latest_user_category_decision AS (
SELECT user_key, consent_category, consent_status, consent_region, country
FROM deduplicated_decisions
QUALIFY ROW_NUMBER() OVER (
PARTITION BY user_key, consent_category
ORDER BY event_timestamp DESC, consent_decision_id DESC
) = 1
),
user_level AS (
SELECT
consent_category,
consent_region,
country,
COUNT(DISTINCT user_key) AS users_with_consent_decision,
COUNT(DISTINCT IF(consent_status = 'granted', user_key, NULL)) AS users_opted_in
FROM latest_user_category_decision
GROUP BY consent_category, consent_region, country
),
decision_level AS (
SELECT
consent_category,
consent_region,
country,
COUNT(*) AS decision_events,
COUNTIF(consent_status = 'granted') AS granted_decision_events
FROM deduplicated_decisions
GROUP BY consent_category, consent_region, country
)
SELECT
u.consent_category,
u.consent_region,
u.country,
u.users_with_consent_decision,
u.users_opted_in,
ROUND(SAFE_DIVIDE(u.users_opted_in, u.users_with_consent_decision) * 100, 2) AS cookie_consent_opt_in_rate_pct,
d.decision_events,
d.granted_decision_events,
ROUND(SAFE_DIVIDE(d.granted_decision_events, d.decision_events) * 100, 2) AS decision_level_opt_in_rate_pct
FROM user_level AS u
LEFT JOIN decision_level AS d
USING (consent_category, consent_region, country)
ORDER BY u.consent_category, u.consent_region, u.country;Adobe Experience Platform (AEP Query Service)
-- ==========================================
-- 2. ADOBE AEP QUERY SERVICE SQL
-- ==========================================
WITH consent_events AS (
SELECT
COALESCE(_tenant.consent.consentId, identityMap['ECID'][0].id) AS user_key,
timestamp AS event_ts,
COALESCE(placeContext.geo.countryCode, 'unknown') AS country,
_tenant.consent.contentType AS content_type,
_tenant.consent.category AS consent_category,
_tenant.consent.status AS consent_status,
_tenant.consent.action AS consent_action,
_tenant.consent.method AS consent_method,
_tenant.consent.version AS consent_version,
COALESCE(_tenant.consent.region, 'unknown') AS consent_region,
_tenant.consent.decisionId AS consent_decision_id
FROM your_aep_web_events_dataset
WHERE timestamp >= TIMESTAMP '2026-03-01 00:00:00'
AND timestamp < TIMESTAMP '2026-04-01 00:00:00'
AND web.webInteraction.name = 'select_content'
AND COALESCE(_tenant.trafficType, '') <> 'internal'
AND NOT (LOWER(COALESCE(environment.browserDetails.userAgent, '')) RLIKE 'bot|crawler|spider|headless')
AND COALESCE(web.webPageDetails.URL, '') NOT LIKE '%staging%'
AND COALESCE(web.webPageDetails.URL, '') NOT LIKE '%://qa.%'
),
explicit_decisions AS (
SELECT
*,
LAG(consent_status) OVER (PARTITION BY user_key, consent_category ORDER BY event_ts) AS prev_status,
LAG(event_ts) OVER (PARTITION BY user_key, consent_category ORDER BY event_ts) AS prev_event_ts
FROM consent_events
WHERE user_key IS NOT NULL
AND content_type IN ('consent_banner', 'preference_center')
AND consent_method IN ('banner', 'preference_center')
AND consent_action IN ('accept_all', 'reject_all', 'save_preferences', 'withdraw')
AND consent_category IN ('analytics', 'functional', 'personalization', 'advertising')
AND consent_status IN ('granted', 'denied')
),
deduplicated_decisions AS (
SELECT *
FROM explicit_decisions
WHERE NOT (
prev_status IS NOT NULL
AND prev_status = consent_status
AND unix_timestamp(event_ts) - unix_timestamp(prev_event_ts) < 5
)
),
ranked_decisions AS (
SELECT
user_key,
consent_category,
consent_status,
consent_region,
country,
ROW_NUMBER() OVER (
PARTITION BY user_key, consent_category
ORDER BY event_ts DESC, consent_decision_id DESC
) AS rn
FROM deduplicated_decisions
),
user_level AS (
SELECT
consent_category,
consent_region,
country,
COUNT(DISTINCT user_key) AS users_with_consent_decision,
COUNT(DISTINCT CASE WHEN consent_status = 'granted' THEN user_key END) AS users_opted_in
FROM ranked_decisions
WHERE rn = 1
GROUP BY consent_category, consent_region, country
),
decision_level AS (
SELECT
consent_category,
consent_region,
country,
COUNT(*) AS decision_events,
SUM(CASE WHEN consent_status = 'granted' THEN 1 ELSE 0 END) AS granted_decision_events
FROM deduplicated_decisions
GROUP BY consent_category, consent_region, country
)
SELECT
u.consent_category,
u.consent_region,
u.country,
u.users_with_consent_decision,
u.users_opted_in,
ROUND(100.0 * u.users_opted_in / NULLIF(u.users_with_consent_decision, 0), 2) AS cookie_consent_opt_in_rate_pct,
d.decision_events,
d.granted_decision_events,
ROUND(100.0 * d.granted_decision_events / NULLIF(d.decision_events, 0), 2) AS decision_level_opt_in_rate_pct
FROM user_level u
LEFT JOIN decision_level d
ON u.consent_category = d.consent_category
AND u.consent_region = d.consent_region
AND u.country = d.country
ORDER BY u.consent_category, u.consent_region, u.country;Source Data
Digital Analytics (GA4) + Consent Management Platform
Dependencies
Prerequisite: List the dependencies required for this Metric
Events:
Dimensions:
Report Attributes
consent_category consent_status consent_method consent_version country region device_category browser page_type traffic_source new_vs_returning_user
Recommended Reporting Views
Segment Eligibility
true
Governance
Derived KPIs
Governance
GitHub
catalog/metrics/cookie-consent-opt-in-rate-by-category.yml