Clickio GDPR Consent Tool

GDPR Consent Tool Overview

We offer full compliance with Google EU user consent policy and with requirements of other demand partners. All publishers using Google's ad products need to obtain end users' consent before showing personalized ads. The records of consent must be retained, and users must be able to revoke consent.

You can find more information about the product and a live demo page at gdpr.clickio.com.

Technical Specifications

CMP API

By default GDPR consent aware codes (including the consent tool itself) are served by our geo-aware CDN only to EEA users, but the publisher can switch on consent-aware codes for all users in the consent settings inside the platform.

1. CDN decides which code to serve EEA or non-EEA (those can be the same, if publishers choose in consent tool settings, then EEA === non-EEA)

2.1. EEA code loads on the page

2.2. non-EEA code loads on the page

How to force the consent dialog to be shown

You can use the following code to forcefully show the consent UI. You do not need to do this unless you are absolutely sure it`s mandatory, otherwise Clickio consent tool shows the popup automatically when needed. Please note that it will only be shown to EEA users.

window.__lxG__consent__.showConsent();

Alternatively, you can add ?showConsent=1 parameter to the page URL, also applies only to EEA users.

 

GDPR Consent Tool Setup

To set up the GDPR Consent Tool on your site, follow the steps below:

Popup position settings is not supported on AMP pages.

pasted-image-0-(5).png

Please note that you need to enter the color in HEX format only.

Additional Settings

Web Implementation Guide

To add Clickio GDPR Consent Tool on your site, follow the steps below:

Step 1: Install сonsent code
Step 2: Add ‘Change privacy settings’ link

To comply with GDPR, publishers must give users a way to change consent settings. A persistent link should be placed on your website, where users can easily find it and edit their consent preferences at any time. A click on this link will show the consent dialog again.

<a href="#" onclick="if(window.__lxG__consent__ !== undefined) {window.__lxG__consent__.showConsent()} else {alert('This function only for users from European Economic Area (EEA)')}; return false">Change privacy settings</a>

 

AMP Implementation Guide

Core principals

When a user visits an AMP page for the first time, AMP framework requests checkConsentHref URL and determines if the user consent is required.

We provide two ways of integration:

  1. Without domain parking. The user has to give consent separately on AMP and non-AMP versions of your site.
  2. With domain parking through CNAME record. This way, both AMP and non-AMP versions of your site can utilize the same user consent data (pop-up shown once).

 

AMP integration without domain parking

To add Clickio GDPR Consent Tool on AMP version of your site, follow the steps below:

Step 1: Install AMP сonsent support library
Step 2: Install consent HTML tag with the ‘Change privacy settings’ link
Step 3: Set up ads pausing

Add the 'data-block-on-consent' attribute to all <amp-ad> or <amp-sticky-ad> tags to prevent them from rendering without consent.

<amp-ad data-block-on-consent ... > ... </amp-ad>
<amp-sticky-ad data-block-on-consent ... > ... </amp-sticky-ad>

 

AMP integration with domain parking

To allow transparent usage of consent cookies in both AMP and non-AMP versions of your site, we offer publishers to park a subdomain which will be used to serve Consent Tool UI iframe in AMP site version, so that cookies can be set/read directly to/from user browser.

This logic works only for sites on https, due to browsers security restrictions

Step 1: Create a subdomain

Create a subdomain to serve Consent Tool UI, e.g. consent.yourdomain.com.

Step 2: Add CNAME record

Create the record consent.yourdomain.com. CNAME consent.clickiocdn.com. in your DNS provider control panel, replacing yourdomain.com with the actual domain name of your site.

Please report the created domain name to your Clickio manager or to our support team, for us to approve it and set up the backend part.

There is no need to change consent script URLs on the www version of your site.

Step 3: Install AMP сonsent support library
Step 4: Install consent HTML tag with the ‘Change privacy settings’ link

Example: 

The URLs specified in the consent config tag are not meant to be called directly, so return 400 http error code in that case.

Step 5: Set up ads pausing

Add the 'data-block-on-consent' attribute to all <amp-ad> or <amp-sticky-ad> tags to prevent them from rendering without consent.

<amp-ad data-block-on-consent ... > ... </amp-ad>
<amp-sticky-ad data-block-on-consent ... > ... </amp-sticky-ad>

 

Implementation Examples

Implementation Examples

CMP API usage

Introduction

As an approved IAB Consent Management Platform, Clickio Consent Tool fully supports the standardized CMP Javascript API for user consent data retrieval.

Non-EU visitors and CMP API

By default, the non-EU version of the Consent Tool code (automatically served by our geo-aware CDN) does not include CMP API functionality (to minimize load traffic/time), but that is server-side configurable - feel free to contact support if you need it.

General information

The current version API documentation can be found here. Clickio CMP API implementation fully supports all the described functionality.

When implementing the CMP API integration, you should bear in mind that it loads asynchronously, so you can not directly call __tcfapi function to pass commands from your scripts. Avoid implementing any await cycles/timeouts, which create parasitic load or delays. Instead, you can use a simple CMP API stub, which gracefully handles all the calls for you, while the full code loads.

<script type="text/javascript">
!function(){"use strict";var t,e=function(t){try{return!!t()}catch(t){return!0}},n=!e((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),r="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},o=function(t){return t&&t.Math==Math&&t},i=o("object"==typeof globalThis&&globalThis)||o("object"==typeof window&&window)||o("object"==typeof self&&self)||o("object"==typeof r&&r)||function(){return this}()||Function("return this")(),a=function(t){return"object"==typeof t?null!==t:"function"==typeof t},c=i.document,f=a(c)&&a(c.createElement),u=!n&&!e((function(){return 7!=Object.defineProperty(("div",f?c.createElement("div"):{}),"a",{get:function(){return 7}}).a})),s=function(t){if(!a(t))throw TypeError(String(t)+" is not an object");return t},p=Object.defineProperty,l=n?p:function(t,e,n){if(s(t),e=function(t,e){if(!a(t))return t;var n,r;if("function"==typeof(n=t.toString)&&!a(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!a(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")}(e),s(n),u)try{return p(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t},d=Function.prototype,y=d.toString,g=/^\s*function ([^ (]*)/;n&&!("name"in d)&&l(d,"name",{configurable:!0,get:function(){try{return y.call(this).match(g)[1]}catch(t){return""}}}),(function(t){t.exports=function(){for(var t,e,n="__tcfapiLocator",r=[],o=window;o;){try{if(o.frames[n]){t=o;break}}catch(t){}if(o===window.top)break;o=o.parent}t||(function t(){var e=o.document,r=!!o.frames[n];if(!r)if(e.body){var i=e.createElement("iframe");i.style.cssText="display:none",i.name=n,e.body.appendChild(i)}else setTimeout(t,5);return!r}(),o.__tcfapi=function(){for(var t=arguments.length,n=new Array(t),o=0;o<t;o++)n[o]=arguments[o];if(!n.length)return r;if("setGdprApplies"===n[0])n.length>3&&2===parseInt(n[1],10)&&"boolean"==typeof n[3]&&(e=n[3],"function"==typeof n[2]&&n[2]("set",!0));else if("ping"===n[0]){var i={gdprApplies:e,cmpLoaded:!1,cmpStatus:"stub"};"function"==typeof n[2]&&n[2](i)}else r.push(n)},o.addEventListener("message",(function(t){var e="string"==typeof t.data,n={};try{n=e?JSON.parse(t.data):t.data}catch(t){}var r=n.__tcfapiCall;r&&window.__tcfapi(r.command,r.version,(function(n,o){var i={__tcfapiReturn:{returnValue:n,success:o,callId:r.callId}};e&&(i=JSON.stringify(i)),t&&t.source&&t.source.postMessage&&t.source.postMessage(i,"*")}),r.parameter)}),!1))}}(t={exports:{}}),t.exports)()}();
</script>

After installing this code on the page, you can then call the API as usual (getTCData dump example is shown below). It is recommended to install the code prior to any other related scripts in the page head.

window.__tcfapi('getTCData', null, function (consentData, consentDataSuccess) {
    console.groupCollapsed('getTCData called ('+consentDataSuccess+')');
    console.log(consentData);
    console.groupEnd();
});

The stub stores the request and replies as soon as the full code loads. PostMessage commands are also supported.

 

Implementation Examples

CMP API integration (Clickio specific)

By default, all publishers are encouraged to use standard TCF v2 API (__tcfapi) to get end-user consent information, such as per-vendor settings, specific purposes consent, etc. This is a future-proof solution if you choose to switch to another Consent Tool solution, as any IAB certified CMP should support this. But Clickio Consent Tool (CCT for short) also offers simplified API for publishers' convenience.

By default, __tcfapi is not available for non-EEA users for optimization reasons. If you choose to utilize it or want to have it anyway, please contact our support.

Clickio simplified API

There are several methods available for your convenience, all are present for both EEA and non-EEA users (based on our geo-aware CDN response), but for EEA users all state methods return actual value only after user consent state is determined, so must be used wisely.

An example of API usage can be found on this page.

Base global object

Before calling any methods we must be sure that CCT global object is instantiated correctly:

consentCallback method

CCT allows a specially named function is defined on the page and will call it once the user consent state is determined:

window.__lxG__consent__.consentCallback = function (consentState) {
  if (consentState === null) {
    // consent not applicable, non-eu user
  } else if (consentState === -1) {
    // eu user, consent interface shown, cmp loaded, user has not decided yet, waiting for user consent
  } else if (consentState === 0) {
	// is not reported from Clickio code, may be used for different timeout fallbacks
  } else if (consentState === 1) {
    // eu user, user consented
	// mind consentState === 1 does not mean, that user consented to everything
  }
};

This callback can be combined with the __tcfapi calls for EEA users, it is called once the CMP API is available for usage.

clickioConsentEvent event

The second method to trigger any logic when the consent state is determined is the clickioConsentEvent event dispatched on the html tag:

let consentCallback = function (consentState) {
  if (consentState === null) {
    // consent not applicable, non-eu user
  } else if (consentState === -1) {
    // eu user, consent interface shown, cmp loaded, user has not decided yet, waiting for user consent
  } else if (consentState === 0) {
	// is not reported from Clickio code, may be used for different timeout fallbacks
  } else if (consentState === 1) {
    // eu user, user consented
	// mind consentState === 1 does not mean, that user consented to everything
  }
};

document.documentElement.addEventListener(
  'clickioConsentEvent',
  function(event){
    // event.detail.state will report the consent state
    consentCallback(event.detail.state);
  },
  false
);

The events approach is handy when having multiple entry points for consent-based logic as you may subscribe to it as many times as needed.

For fallback calls (such as timeouts), you may use the following code:

document.documentElement.dispatchEvent(new CustomEvent('clickioConsentEvent', {'detail': {'state': 0}}));
showConsent method

This method is used to trigger a consent popup UI display and is intended only for usage inside "Change privacy settings" link on the page. Do not call it manually unless you are rock-solid sure that you need it. The consent popup UI is shown automatically when/if it is needed on every page where CCT js is included. Of course, the event on manual call,  the UI will be shown only for EEA users, as the logic is not present in the non-EEA code version.

// show Clickio consent popup
window.__lxG__consent__.showConsent();
getState method

This method is used to get user consent state, the same which is passed to consentCallback method and clickioConsentEvent event. The possible values are the same:

let consentState = window.__lxG__consent__.getState();

if (consentState === null) {
  // consent not applicable, non-eu user
} else if (consentState === -1) {
  // eu user, consent interface shown, cmp loaded, user has not decided yet, waiting for user consent
} else if (consentState === 1) {
  // eu user, user consented
  // mind consentState === 1 does not mean, that user consented to everything
}
getGoogleConsentMode method

This method is used to get Google vendor state based on user consent permissions:

let googleConsent = window.__lxG__consent__.getGoogleConsentMode();

if (googleConsent === null) {
  // consent state unknown, user have not consented yet
} else if (googleConsent === 0) {
  // user has declined consent for Google Inc. or not all needed permissions given
} else if (googleConsent === 1) {
  // user has allowed Google non-personalized ads
  // conditions met: https://support.google.com/admanager/answer/9805023?hl=en
  // there is no need to explicitly call googletag.pubads().setRequestNonPersonalizedAds(0)
  // Google gets the information from consent string
} else if (googleConsent === 2) {
  // user has allowed Google personalized ads
  // conditions met: https://support.google.com/admanager/answer/9805023?hl=en
  // there is no need to explicitly call googletag.pubads().setRequestNonPersonalizedAds(1)
  // Google gets the information from consent string  
} else if (googleConsent === 3) {
  // user has allowed Google requests
  // neither of conditions met: https://support.google.com/admanager/answer/9805023?hl=en
  // but there is consent for Google Inc. vendor and Purpose 1
}
getPurposeOneAllowed method

This method is to determine user consent for Purpose 1 exclusively, it can be used to call different tracker pixels, etc.

let purposeOneConsent = window.__lxG__consent__.getPurposeOneAllowed();

if (purposeOneConsent === null) {
  // consent state unknown, user have not consented yet
} else if (purposeOneConsent === 0) {
  // no user consent for Purpose 1
} else if (purposeOneConsent === 1) {
  // user has consented to Purpose 1
}

 

 

Implementation Examples

CMP script tag integration (Clickio specific)

By default, all publishers are encouraged to use the standard TCF v2 API (__tcfapi) to get end-user consent information, such as per-vendor settings, specific purposes consent, etc. However, Clickio Consent also offers simplified method for publishers' convenience.

By default, __tcfapi is not available for non-EEA users for optimization reasons. If you choose to utilize it or want to have it anyway, please contact our support.

 

Clickio simplified integration

To pause script tags execution before user consent is obtained, we offer a simplified solution, which also allows additional consent data checks before the script is executed

This solution is fine for most cases, but if you can integrate using TCF or Clickio APIs, it is better to use those approaches, as those provide more granular control over the process and consent data

If you plan to pause script tags with src attribute, consider preloading those with:
<link rel="preload" href="%scipr_src_here%" as="script" />
to minimize delays when script is executed

Only script tags with attribute async are processed by the Consent tool logic, as the code itself is executed asynchronously, please double-check to prevent erroneous behaviour (scripts without attribute will not be executed, execution logic may change on async script execution)

 
Pausing script tag before user choice is obtained

For example you have the following code on your site (works the same way for tags with src attribute):

<script type="text/javascript">
console.log('user choice obtained');
</script>

1. Add async attribute to the tag

2. Set tag type attribute to text/clickiocmp

Resulting code:

<script async type="text/clickiocmp">
console.log('user choice obtained');
</script>

This code will be executed automatically when the user choice is obtained.

 

To fine-control script execution conditions we have provided several additional attributes:

The following attributes can be combined and the check is performed with AND condition.

 
clickio-cmp-purpose-consent

Can be an integer or a comma separated list of integers of IDs of IAB specified purposes for which consent is mandatory for script execution.

Example:

<script async type="text/clickiocmp" clickio-cmp-purpose-consent="1">
console.log('user consent for purpose 1 obtained');
</script>

 

clickio-cmp-purpose-li

Can be an integer or a comma separated list of integers of IDs of IAB specified purposes for which legitimate interest approval is mandatory for script execution.

Example:

<script async type="text/clickiocmp" clickio-cmp-purpose-li="2,7,9,10">
console.log('user approved legitimate interest for purposes 2, 7, 9 and 10');
</script>

 

clickio-cmp-vendor-consent

Can be an integer or a comma separated list of integers of IDs of IAB vendors for which consent is mandatory for script execution.

Example:

<script async type="text/clickiocmp" clickio-cmp-vendor-consent="755">
console.log('user consent for Google Inc. (id:755) obtained');
</script>

 

clickio-cmp-vendor-li

Can be an integer or a comma separated list of integers of IDs of IAB vendors for which legitimate interest approval is mandatory for script execution.

Example:

<script async type="text/clickiocmp" clickio-cmp-vendor-li="755">
console.log('user approved legitimate interest for Google Inc. (id:755)');
</script>

 

Implementation Examples

Pausing Google AdSense and Ad Manager tags

If you intend to use non-Clickio Google-based ads codes on the page with the Clickio Consent Tool, those may not be paused because of different reasons, to help you with that, you can use one of the codes provided here to make it work. Just add the chosen code to the head of the page before any other ad serving code (including Clickio Consent Tool code).

For more information, technical explanations, and custom integrations example please refer to this article.

AdSense
<script type="text/javascript">
  var adsbygoogle=window.adsbygoogle||[];
  adsbygoogle.pauseAdRequests=1;
  
  var consentCallbackQueue=function(e,o){var t=!1,n=!1;let a=[],g=!1,l=!1,s=function(){g=!0,a.map(function(e,o){void 0!==e&&(e(t,n),a[o]=void 0)})};return document.documentElement.addEventListener("clickioConsentEvent",function(o){var a;a=o.detail.state,l=!0,null===a?(t=!0,n=!0,s()):-1===a||(0===a?(t=!1,n=!1,s()):1===a&&(t=!!e.__lxG__consent__.getGoogleConsentMode(),n=!!e.__lxG__consent__.getPurposeOneAllowed(),s()))},!1),setTimeout(function(){l||document.documentElement.dispatchEvent(new CustomEvent("clickioConsentEvent",{detail:{state:0}}))},5e3),{push:function(e){g?e(t,n):a.push(e)}}}(window);
   
  consentCallbackQueue.push(function(e,o){e&&(adsbygoogle.pauseAdRequests=0)});
</script>
Ad Manager
<script type="text/javascript">
  var googletag=googletag||{};
  googletag.cmd=googletag.cmd||[];
  googletag.cmd.unshift(function(){window.googletag.pubads().disableInitialLoad()});

  var consentCallbackQueue=function(e,o){var t=!1,n=!1;let a=[],g=!1,l=!1,s=function(){g=!0,a.map(function(e,o){void 0!==e&&(e(t,n),a[o]=void 0)})};return document.documentElement.addEventListener("clickioConsentEvent",function(o){var a;a=o.detail.state,l=!0,null===a?(t=!0,n=!0,s()):-1===a||(0===a?(t=!1,n=!1,s()):1===a&&(t=!!e.__lxG__consent__.getGoogleConsentMode(),n=!!e.__lxG__consent__.getPurposeOneAllowed(),s()))},!1),setTimeout(function(){l||document.documentElement.dispatchEvent(new CustomEvent("clickioConsentEvent",{detail:{state:0}}))},5e3),{push:function(e){g?e(t,n):a.push(e)}}}(window);
  
  function displayAndRefreshSlotById(e){consentCallbackQueue.push(function(o,t){o&&googletag.cmd.push(function(){googletag.pubads().isInitialLoadDisabled()?googletag.pubads().getSlots().forEach(function(o){o.getSlotElementId()==e&&(googletag.display(e),googletag.pubads().refresh([o]))}):googletag.display(e)})})}
</script>

Mind that for the correct functioning of the code (i.e. ad slots refresh after consent state is determined) you should also replace the current Ad Manager display codes:

<script type="text/javascript">
googletag.cmd.push(function(){
	googletag.display('div-id-1');
});
</script>

with the provided wrapper code:

<script type="text/javascript">
displayAndRefreshSlotById('div-id-1');
</script>

This code will not call googletag.display before user consent, once it is received, it will push the command to the googletag queue and also call refresh for the slot, if isInitialLoadDisabled is true. If any tags are not replaced with the provided wrapper code (i.e. 3rd party codes), you should manually call refresh for the slot using the consentCallbackQueue.

AdSense & Ad Manager simultaneously

<script type="text/javascript">
  var googletag=googletag||{};
  googletag.cmd=googletag.cmd||[];
  googletag.cmd.unshift(function(){window.googletag.pubads().disableInitialLoad()});
  
  var adsbygoogle=window.adsbygoogle||[];
  adsbygoogle.pauseAdRequests=1;
  
  var consentCallbackQueue=function(e,o){var t=!1,n=!1;let a=[],g=!1,l=!1,s=function(){g=!0,a.map(function(e,o){void 0!==e&&(e(t,n),a[o]=void 0)})};return document.documentElement.addEventListener("clickioConsentEvent",function(o){var a;a=o.detail.state,l=!0,null===a?(t=!0,n=!0,s()):-1===a||(0===a?(t=!1,n=!1,s()):1===a&&(t=!!e.__lxG__consent__.getGoogleConsentMode(),n=!!e.__lxG__consent__.getPurposeOneAllowed(),s()))},!1),setTimeout(function(){l||document.documentElement.dispatchEvent(new CustomEvent("clickioConsentEvent",{detail:{state:0}}))},5e3),{push:function(e){g?e(t,n):a.push(e)}}}(window);
  
  function displayAndRefreshSlotById(e){consentCallbackQueue.push(function(o,t){o&&googletag.cmd.push(function(){googletag.pubads().isInitialLoadDisabled()?googletag.pubads().getSlots().forEach(function(o){o.getSlotElementId()==e&&(googletag.display(e),googletag.pubads().refresh([o]))}):googletag.display(e)})})}
  
  consentCallbackQueue.push(function(e,o){e&&(adsbygoogle.pauseAdRequests=0)});
</script>

You should also replace Ad Manager display codes with the wrapper code as explained in the Ad Manager section.

 

Implementation Examples

Pausing Google Tag Manager tags

Step 1

Place this code in <head></head> section of the site, after our Consent Tool line and before GTM script:

<script>
(window.__lxG__consent__ = window.__lxG__consent__ || {}).consentCallback = function (state) {
if (state !== -1) {(window.dataLayer = window.dataLayer || []).push({'event': 'consentReady'});}};
</script>
Step 2

Create a custom event trigger with Event name "consentReady": img01.png

Step 3

Add a new trigger in the Triggering section of the tag: img02.png

Read more about custom event triggers in Google Tag Manager.

 

Implementation Examples

Troubleshooting ad pausing and unpausing

Introduction
Ad Manager
AdSense and Ad Exchange

Please refer to this article for additional methods of pausing AdSense or Ad Manager tags

Why ads might fail to pause

The main reasons why the Consent Tool might not pause the ads, along with solutions, are listed below.

1. The ads are called before the Consent Tool is initiated.

In certain cases, ad units might be called before the Consent Tool has a chance to invoke the corresponding pausing mechanism.

2. Some other code on the page initiates refresh() of Ad Manager ad units.

Other scripts on the page can call refresh() of ad units. You can use Google Publisher Console to detect this behavior.

3. Advertising is shown by ad tags other than Ad Manager and AdSense

By default, the Consent Tool only pauses Ad Manager and AdSense tags. If you have tags from other vendors on the site, they can communicate with the Consent Tool using CMP Javascript API, which is a standard part of the IAB's Transparency and Consent Framework. Alternatively, the tags can be paused and unpaused using the Consent Tool's simplified API.

If you use Google Tag Manager to show ad tags, please refer to our GTM integration guide.

Some ad units fail to show

After the user's decision, the Consent Tool refreshes all ad units on the page. If new ad units are dynamically added on the page later (for example, when the new content is loaded in an infinite scroll), they need to be refreshed manually using refresh() method after calling display().

Callback queue pausing solution

To help you solve more complicated cases, we've created the ultimate weapon - a queue-based pausing/unpausing solution.

Based on the Clickio simplified consent API

The following code will allow pausing any ads but is customized for Ad Manager and AdSense. Any parts can be omitted if there is no need for them.

// initialize AdManager globals
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
// disable initial load for AdManager
googletag.cmd.unshift(function () {
    window.googletag.pubads().disableInitialLoad();
});

// initialize AdSense globals
var adsbygoogle             = window.adsbygoogle || [];
// pause AdSense requests
adsbygoogle.pauseAdRequests = 1;

// Consent tool logic ready queue
var consentCallbackQueue = (function (window, undefined) {
    // semaphores default values
    // Google requests allowed (Google Inc. and Purpose 1 consented)
    var clickioConsentGoogleAllowed     = false;
    // Purpose 1 consented (allows using cookies)
    var clickioConsentPurposeOneAllowed = false;

    let timeoutCallInSeconds = 5; // false to disable

    let queue                 = [];
    let startImmediately      = false;
    let consentCallbackCalled = false;

    let addFunction = function (callback) {
        if (startImmediately) {
            callback(clickioConsentGoogleAllowed, clickioConsentPurposeOneAllowed);
        } else {
            queue.push(callback);
        }
    };

    let runQueue = function () {
        startImmediately = true;
        queue.map(function (callback, i) {
            if (callback !== undefined) {
                callback(clickioConsentGoogleAllowed, clickioConsentPurposeOneAllowed);
                queue[i] = undefined;
            }
        });
    };

    // callback which is executed by CCT code upon consent state determination

    let consentCallback = function (consentState) {
        consentCallbackCalled = true;

        if (consentState === null) {
            // consent not applicable, non-eu user, executing queue right away

            clickioConsentGoogleAllowed     = true;
            clickioConsentPurposeOneAllowed = true;

            runQueue();
        } else if (consentState === -1) {
            // eu user, consent interface shown, cmp loaded, user has not decided yet, waiting for user consent
        } else if (consentState === 0) {
            // eu user, consent callback timeout, executing the queue

            // google does not serve ads for GDPR users without consent and reports errors - do not allow Google in this case
            clickioConsentGoogleAllowed     = false;
            clickioConsentPurposeOneAllowed = false;

            runQueue();
        } else if (consentState === 1) {
            // eu user, user consented, executing the queue
            // !IMPORTANT! consentState === 1 DOES NOT MEAN THAT USER ALLOWED EVERYTHING

            clickioConsentGoogleAllowed     = !!window.__lxG__consent__.getGoogleConsentMode();
            clickioConsentPurposeOneAllowed = !!window.__lxG__consent__.getPurposeOneAllowed();

            runQueue();
        }
    };

    document.documentElement.addEventListener(
        'clickioConsentEvent',
        function(event){ consentCallback(event.detail.state); },
        false
    );

    // in case of network problems in loading consent.js
    if (timeoutCallInSeconds) {
        setTimeout(function () {
            if (!consentCallbackCalled) {
                // calling function to trigger logic
                document.documentElement.dispatchEvent(new CustomEvent('clickioConsentEvent', {detail: {state: 0}}));
            }
        }, timeoutCallInSeconds*1000);
    }

    return {
        push: addFunction
    };
})(window);

// function to display AdManager slot by it`s div ID
// it supports automatic slot refresh if disableInitialLoad was called before
// respects end user consent settings
function displayAndRefreshSlotById(slotId) {
    consentCallbackQueue.push(function (googleAllowed, purposeOneAllowed) {
        if (googleAllowed) {
            googletag.cmd.push(function () {
                if (googletag.pubads().isInitialLoadDisabled()) {
                    googletag.pubads().getSlots().forEach(function (slot) {
                        if (slot.getSlotElementId() == slotId) {
                            googletag.display(slotId);
                            googletag.pubads().refresh([slot]);
                        }
                    });
                } else {
                    googletag.display(slotId);
                }
            });
        }
    });
}

// unpause AdSense
// respects end user consent settings
consentCallbackQueue.push(function (googleAllowed, purposeOneAllowed) {
    if (googleAllowed) {
        adsbygoogle.pauseAdRequests = 0;
    }
});

The core of the logic is a special callbacks queue, which is consecutively executed upon consent determination (either user decides in the UI or consent state is read from the cookies). This queue mechanism is also fully functional for non-EU users, so no need for geo-based logic branching.

In the example above, there is also a utility function (displayAndRefreshSlotById) which incorporates adding callback to the consent queue and triggering display and refresh for the Ad  Manager slot found by its div ID. The implementation may vary for other advertising systems.

AdSense and Ad Manager pausing codes are based on the provided queue-based solution

 

Google TCF v2.0 Error Report

The document is being updated as we get more details on Google’s TCF implementation and monitoring.

On August 15, 2020, IAB Transparency and Consent Framework fully migrated from version 1.1 to version 2.0. 

At the same time, Google joined TCF v2.0 as a vendor and started monitoring publisher compliance with TCF Policies. The results of this monitoring are presented in TCF error reports in AdSense and Ad Manager accounts.

Most publisher accounts would have some errors flagged in their TCF error report. For example, content served from cache or 3rd party services might result in Google not recognizing the domain or the TC string being out-of-date. 

For this reason, it is important to compare the number of events in the report with the 7-day impression average, and investigate errors that cover a meaningful share of impressions. 

The most common issues highlighted in the report are the following.

Error

Description

Comment

Monetization status

Unknown domain

Ad requests detected from a domain that is not registered in the account. 

A small percentage of such requests is normal, as some users can access the site’s content from caches of 3rd party web services.

Often, error 1.1 is stated for Unknown domain events. In this case, the ad requests are not filled. 

Error 1.1

Google, as a vendor, is not allowed under consent or legitimate interest.

Google couldn’t detect consent or legitimate interest signals. This could be a result of an outdated CMP configuration, or a user intentionally rejecting Google as a vendor.

Ad requests are not filled.

Error 2.1a

Tag or SDK isn’t receiving a TC string due to CMP status being stub, loading, or error.

The most common reason is ads being requested before the user decision was made. If you are using Clickio CMP with “Pause ads while the user is choosing” setting enabled, but are still receiving this error, refer to this troubleshooting guide. If you are using another CMP, check how to implement ad pausing with the vendor.

For 30 days from August 15, 2020, impressions are monetized as usual. For 60 days after this period, non-personalized ads will be shown. After, these ad requests will not be filled.

Error 5.2

Globally-scoped TC string.

Clickio CMP only obtains service-specific consent, in compliance with Google’s interpretation of GDPR. If you are using another CMP, instruct it to collect service-specific consent instead of global consent. 

Google continues to monetize this traffic subject to limitations during 90 days from August 15, 2020. After, these ad requests will not be filled.

More information on errors can be found in Google's TCF v2 troubleshooting guide. Please contact Clickio support if you have more questions.

Frequently Asked Questions

I received a TCF error notification from Google. Did it happen because Clickio updated the Consent tool?

Google started monitoring TCF compliance from August 15, after joining TCF. Because of this, publishers started to receive notifications. Before this, Google was not a part of TCF and did not conduct any checks.

Clickio, as other CMP vendors, upgraded the consent solution to comply with the new version of TCF at about the same time. 

What is IAB Transparency and Consent Framework (TCF)?

In order to make the GDPR compliance process coherent across the market, IAB Europe developed the “Transparency and Consent Framework”; a standard to easily collect consent from users and share it with the rest of the supply chain.

According to IAB, there are three kinds of companies involved in the process: publishers, vendors (tech providers such as DSPs, SSPs, DMPs, ad servers etc.) and CMPs (Consent Management Provider, i.e. companies that can read and/or set a user’s consent status for the vendors chosen by a website operator, and share this information within the advertising ecosystem).

By applying this framework, publishers can inform their users of what data is being collected, what vendors are going to use it and why. For each one of these items, users can give or deny consent, and their choice can then be shared with other players in the advertising market.

What is IAB Transparency and Consent Framework (TCF) v2.0?

In August last year, IAB Europe announced the launch of the Transparency and Consent Framework (TCF) v2.0, the second version of the framework. This resulted from a 12-month review period, during which IAB collected and implemented feedback from all sectors of the digital advertising industry (notably publishers), and had meetings with Data Protection Authorities (DPAs) throughout Europe.

Among the main updates of TCF v2.0 there’s now the possibility for consumers to authorize or deny consent, as well as to exercise the “right to object” for their data to be processed. Also, consumers now have more control over whether and how vendors can use certain features of data processing, such as precise geolocation.

TCF v2.0 provides benefits to publishers too, giving them more control and flexibility on how to integrate and collaborate with their tech partners. For example, now publishers can manage data processing purposes on a vendor-basis. Furthermore, they can ensure more transparency for users, thanks to the introduction of more clear and detailed purposes for data processing and a higher flexibility in the way these purposes are described.

 

Frequently Asked Questions

Can I add my own disclosures and additional partners?
Yes, you can modify all the texts in the consent interface, and add custom detailed disclosures using “Introduction” and “Custom disclosures” fields.

Can users revoke consent using the tool?
Yes. We provide publishers with the link that must be placed on the site. When the user clicks on the link, the consent dialog reappears.

Clickio participates in the Framework as a registered Consent Management Provider (CMP ID 63). We are fully compatible with the framework.

How often is the IAB vendor list updated?
We store a cached version of the IAB vendor list to speed up delivery, but it is updated every few hours.

How long is the consent stored for? In which cases the consent dialog is shown again?
The user’s privacy choice is remembered for 13 months. The consent dialog will be shown before in the following cases.

  1. The user clicks on the “Change Privacy Settings” link.
  2. The publisher changes the disclosure version in the Consent Tool settings.
  3. New vendors have been added to the IAB vendor list and a certain number of days have passed since the last user decision. According to the GDPR, when new vendors are added, the publisher needs to ask the user for consent again, in order to pass personal information to new vendors. Once the user gives consent to a particular version of the IAB vendor list, the Consent Tool will not ask the user again for a certain number of days, even if the vendor list changes. However, after some time, the tool will prompt the consent dialog again, so that the site can utilize new vendors. This specific interval depends on how frequently the IAB list changes and the relative importance of added vendors.

There is a problem with the way pop-ups look on my site.
In rare cases, the site’s HTML/CSS can interfere with the way pop-ups look. If there is a problem, please contact support and we will resolve it as fast as possible.