> For the complete documentation index, see [llms.txt](https://docs.exads.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.exads.com/general/iab-tcfv2.0/tcf-gdpr-compliant-for-ad-types.md).

# How to Be TCF/GDPR Compliant for Ad Types

How consent is passed for ad types that do not use ad-provider.js: In-Stream VAST, Direct Link, and RTB.

Most ad types use ad-provider.js, which automatically processes the TC String to determine whether consent has been given. When an ad request is generated, ad-provider.js checks for the presence of a CMP. If a CMP is detected, the resulting TC String is analyzed to determine the end user's consent. If consent is granted, the ad is displayed. If consent is not given, the ad is shown using only limited information. Three ad types do not use ad-provider.js, so the consent parameters must be passed explicitly:

* **In-Stream VAST**
* **Direct Link**
* **RTB**

In-Stream VAST and Direct Link pass them in the ad tag URL. RTB passes them in the bid request.

## In-Stream VAST and Direct Link <a href="#in-stream-vast-and-direct-link" id="in-stream-vast-and-direct-link"></a>

The In-Stream VAST tag currently looks like this:

```
https://s.adservingdomain.com/v1/vast.php=123456
```

This endpoint provides a response in the [VAST XML format](/publishers/vast/vast-tag.md). The vast.php endpoint may set cookies and the tracker URLs within the VAST response may also set cookies as well. The Ad Server processes information in the cookies from this endpoint and on tracker URLs and sets them depending on TCF parameters.

The Direct Link tag currently looks like this:

```html
<a href="https://s.zlink0.com/v1/d.php?z=2834828" target="_blank">Click Here!</a>
```

Two parameters enable the use of consent to determine whether cookies should be set. These parameters can be passed to the VAST endpoint and Direct Link endpoint, in the same way described in [What is a TC String](/general/iab-tcfv2.0/general-iab-tcfv2.0.md#what-is-a-tc-string):

* `gdpr`: contains a flag (0 or 1) that indicates whether GDPR and ePrivacy apply.
* `gdpr_consent`: contains the TC String (base64 string).

The endpoint with parameters would look like this:

**In-Stream VAST**

```
https://s.adservingdomain.com/v1/vast.php?idzone=123456&gdpr=1&gdpr_consent=CQLvHAAQLvHAAAcABBENBZFgAAAAAAAAAChQAAAAAAAA.YAAAAAAAAAAA
```

**Direct Link**

```
https://s.zlink0.com/v1/d.php?z=2834828&gdpr=1&gdpr_consent=CQLvHAAQLvHAAAcABBENBZFgAAAAAAAAAChQAAAAAAAA.YAAAAAAAAAAA
```

### Retrieving and Passing TCF Parameter Values <a href="#retrieving--passing-tcf-parameter-values" id="retrieving--passing-tcf-parameter-values"></a>

TCF v2 requires Publishers to implement a Consent Management Platform (CMP) on their website. There are many different CMPs available, each with its own specific instructions for installation and setup on the Publisher's site. CMPs have to provide an API that is defined by the [TCF standard](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#how-does-the-cmp-provide-the-api). This API should be used to retrieve the parameters and pass them into VAST tag. These values should come from [TCData](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#tcdata) object that comes from API:

* `gdpr`: `tcData.gdprApplies`&#x20;
* `gdpr_consent`: `tcData.tcString`

**In-Stream VAST Example Code**

The example below shows how to use `__tcfapi`  together with FluidPlayer to extract these parameters and serve VAST ads.

1. Include the scripts for CMP in the  `<head>`. This example is from OneTrust; different CMPs will require different code:

```html
<head>
    <!-- OneTrust Cookies Consent Notice start for webmasterize.com -->
    <script src="https://cdn.cookielaw.org/consent/tcf.stub.js" type="text/javascript" charset="UTF-8"></script>
    <script src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js"  type="text/javascript" charset="UTF-8" data-domain-script="019373c8-4c4c-7646-bf2a-a947cdf8ec31-test" ></script>
    <script type="text/javascript">
        function OptanonWrapper() { }
    </script>
    <!-- OneTrust Cookies Consent Notice end for webmasterize.com -->
</head>
```

2. Include the video elements that FluidPlayer will be initialized on.

```html
<div class='test'>
    VIDEO 1:
        <video id='my-video1' controls style="width: 640px; height: 360px;">
            <source src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' title='720p' type='video/mp4'>
        </video>
    VIDEO 2:
        <video id='my-video2' controls style="width: 640px; height: 360px;">
            <source src='https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' title='720p' type='video/mp4'>
        </video>
</div>
```

3. Provide the TCFListener object. This is player-agnostic, and could be used for different implementations (uses `__tcfapi`  as defined in the TCF standard).

```js
TCFListener  = (() => {
    // Function that returns a promise to get updated TCF tag
    function getAdsLinkWithTCF(originalLink, reinitializeCallback) {
      return new Promise((resolve) => {
        if (typeof __tcfapi !== 'undefined') {
          // Add a listener for consent changes
          __tcfapi('addEventListener', 2, (tcData, success) => {
            if (success && tcData) {
              console.log('TCF Event ' + tcData.eventStatus + ' Detected:', tcData);
              resolve(updateTag(true, tcData, originalLink, reinitializeCallback));
            } else {
              console.warn('TCF Consent Data unavailable or listener failed.');
              resolve(updateTag(true, null, originalLink, reinitializeCallback));
            }
          });
        } else {
          console.warn('TCF API not available.');
          resolve(updateTag(false, null, originalLink, reinitializeCallback));
        }
      });
    }
    // Private function to assign the correct ad tag based on consent 
    function updateTag(tcfApiDetected, tcData, originalLink, reinitializeCallback) {
      let linkWithTcf;
      if (!tcfApiDetected) {
        linkWithTcf = originalLink + '&gdpr=0';
        console.log('Assigned Ads Link:', linkWithTcf);
        return linkWithTcf;
      }
      let gdprApplies = 1;
      if (tcData && typeof tcData.gdprApplies != "undefined") {
        gdprApplies = (tcData.gdprApplies) ? 1 : 0;
      }
      // TCF API is detected, but user didn't make a selection, we may not have proper consent string, yet.
      linkWithTcf =  originalLink + '&gdpr=' + gdprApplies;
      //User made a selection or their preferences were loaded from the time they previously accessed the page.
      if (tcData && (tcData.eventStatus === 'tcloaded' || tcData.eventStatus === 'useractioncomplete')) {
        if (tcData.tcString) {
          // Pass TC Sting with consent data
          linkWithTcf = originalLink + '&gdpr=' + ((tcData.gdprApplies) ? 1 : 0) + '&gdpr_consent=' + tcData.tcString;
          if (typeof reinitializeCallback !== "undefined") {
            reinitializeCallback(linkWithTcf);
          }
          __tcfapi('removeEventListener', 2, function () {}, tcData.listenerId);
        }
      }
      console.log('Assigned Ads Link:', linkWithTcf);
      return linkWithTcf;
    }
    return {
      "getAdsLinkWithTCF": getAdsLinkWithTCF
    };
  })();
```

4. Provide functions to initialize and re-initialize player instance (this example is specific for FluidPlayer).

```html
<script type="text/javascript">
    // Main Function to start players
    (async () => {
        try {
            /**SETTING UP PLAYER 1**/
            //1) We need function to initialize player by our video element and VAST tag
            let playerInfoReference1 = {}; //reference object to help retain some useful details for player reinitialization
            let initPlayer1 = getPlayerInitializer(playerInfoReference1);
            //2) We need function to reinitialize player with new VAST tag when user selects their preferences
            let reinitPlayer1 = getPlayerReinitializer(playerInfoReference1);
            //3) We need our original VAST tag to pass to TCFListener to append TCF parameters to it.
            // If it's the first time user is visiting the page - the preferences are not selected, so this will likely resolve with default value (no consent).
            // We will need to reinitialize player with actual consent data after users selects cookie preferences, hence passing the function to do that.
            let vastTagWithTCF1 = await TCFListener.getAdsLinkWithTCF('https://s.magsrv.com/v1/vast.php?idzone=5459060', reinitPlayer1);
            //4) We call the first initialization of a player.
            initPlayer1('my-video1', vastTagWithTCF1);
            /**SETTING UP PLAYER 2**/
            //All same as above, this just demonstrates that this works on multiple player instances
            let playerInfoReference2 = {};
            let initPlayer2 = getPlayerInitializer(playerInfoReference2);
            let reinitPlayer2 = getPlayerReinitializer(playerInfoReference2);
            let vastTagWithTCF2 = await TCFListener.getAdsLinkWithTCF('https://s.magsrv.com/v1/vast.php?idzone=3690525', reinitPlayer2);
            initPlayer2('my-video2', vastTagWithTCF2);
        } catch (error) {
            console.error('An error occurred while initializing the TCF listener or player:', error);
        }
    })();
</script>
```

5. Use the TCFListener to get VAST tag updated with TCF parameters and initialize the player. Also provide a way to have player re-initialized once user selects preferences.

```html
<script type="text/javascript">
    // Main Function to start players
    (async () => {
        try {
            /**SETTING UP PLAYER 1**/
            //1) We need function to initialize player by our video element and VAST tag
            let playerInfoReference1 = {}; //reference object to help retain some useful details for player reinitialization
            let initPlayer1 = getPlayerInitializer(playerInfoReference1);
            //2) We need function to reinitialize player with new VAST tag when user selects their preferences
            let reinitPlayer1 = getPlayerReinitializer(playerInfoReference1);
            //3) We need our original VAST tag to pass to TCFListener to append TCF parameters to it.
            // If it's the first time user is visiting the page - the preferences are not selected, so this will likely resolve with default value (no consent).
            // We will need to reinitialize player with actual consent data after users selects cookie preferences, hence passing the function to do that.
            let vastTagWithTCF1 = await TCFListener.getVASTTagWithTCF('https://s.magsrv.com/v1/vast.php?idzone=5459060', reinitPlayer1);
            //4) We call the first initialization of a player.
            initPlayer1('my-video1', vastTagWithTCF1);
            /**SETTING UP PLAYER 2**/
            //All same as above, this just demonstrates that this works on multiple player instances
            let playerInfoReference2 = {};
            let initPlayer2 = getPlayerInitializer(playerInfoReference2);
            let reinitPlayer2 = getPlayerReinitializer(playerInfoReference2);
            let vastTagWithTCF2 = await TCFListener.getVASTTagWithTCF('https://s.magsrv.com/v1/vast.php?idzone=3690525', reinitPlayer2);
            initPlayer2('my-video2', vastTagWithTCF2);
        } catch (error) {
            console.error('An error occurred while initializing the TCF listener or player:', error);
        }
    })();
</script>
```

**Direct Link Example Code**

The example below shows how to use `__tcfapi` to extract these parameters and update a Direct Link tag to include consent information.

1. Include the scripts for CMP in the `<head>`. This example is from OneTrust, different CMPs will require different code here:

```html
<head>
    <!-- OneTrust Cookies Consent Notice start for webmasterize.com -->
    <script src="https://cdn.cookielaw.org/consent/tcf.stub.js" type="text/javascript" charset="UTF-8"></script>
    <script src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js"  type="text/javascript" charset="UTF-8" data-domain-script="019373c8-4c4c-7646-bf2a-a947cdf8ec31-test" ></script>
    <script type="text/javascript">
        function OptanonWrapper() { }
    </script>
    <!-- OneTrust Cookies Consent Notice end for webmasterize.com -->
</head>
```

2. Include Direct Link Tag

```html
<div class='test'>
   <a id='direct-link-1' href="https://s.zlink0.com/v1/d.php?z=2834828" target="_blank">Click Here!</a>
</div>
```

3. Provide the TCFListener object. This is implementation-agnostic and can be used in various contexts, as it uses the `__tcfapi` defined in the TCF standard.

```js
TCFListener  = (() => {
    // Function that returns a promise to get updated TCF tag
    function getAdsLinkWithTCF(originalLink, reinitializeCallback) {
      return new Promise((resolve) => {
        if (typeof __tcfapi !== 'undefined') {
          // Add a listener for consent changes
          __tcfapi('addEventListener', 2, (tcData, success) => {
            if (success && tcData) {
              console.log('TCF Event ' + tcData.eventStatus + ' Detected:', tcData);
              resolve(updateTag(true, tcData, originalLink, reinitializeCallback));
            } else {
              console.warn('TCF Consent Data unavailable or listener failed.');
              resolve(updateTag(true, null, originalLink, reinitializeCallback));
            }
          });
        } else {
          console.warn('TCF API not available.');
          resolve(updateTag(false, null, originalLink, reinitializeCallback));
        }
      });
    }
    // Private function to assign the correct ad tag based on consent
    function updateTag(tcfApiDetected, tcData, originalLink, reinitializeCallback) {
      let linkWithTcf;
      if (!tcfApiDetected) {
        linkWithTcf = originalLink + '&gdpr=0';
        console.log('Assigned Ads Link:', linkWithTcf);
        return linkWithTcf;
      }
      let gdprApplies = 1;
      if (tcData && typeof tcData.gdprApplies != "undefined") {
        gdprApplies = (tcData.gdprApplies) ? 1 : 0;
      }
      // TCF API is detected, but user didn't make a selection, we may not have proper consent string, yet.
      linkWithTcf =  originalLink + '&gdpr=' + gdprApplies;
      //User made a selection or their preferences were loaded from the time they previously accessed the page.
      if (tcData && (tcData.eventStatus === 'tcloaded' || tcData.eventStatus === 'useractioncomplete')) {
        if (tcData.tcString) {
          // Pass TC Sting with consent data
          linkWithTcf = originalLink + '&gdpr=' + ((tcData.gdprApplies) ? 1 : 0) + '&gdpr_consent=' + tcData.tcString;
          if (typeof reinitializeCallback !== "undefined") {
            reinitializeCallback(linkWithTcf);
          }
          __tcfapi('removeEventListener', 2, function () {}, tcData.listenerId);
        }
      }
      console.log('Assigned Ads Link:', linkWithTcf);
      return linkWithTcf;
    }
    return {
      "getAdsLinkWithTCF": getAdsLinkWithTCF
    };
  })();
```

4. Use the TCFListener to get the Direct Link tag updated with TCF parameters and change the link.

```html
<script type="text/javascript">
    // Main Function to start players
    (async () => {
        try {
      /**SETTING UP DIRECT LINK **/
      document.getElementById('direct-link-1').href = await TCFListener.getAdsLinkWithTCF('https://s.zlink0.com/v1/d.php?z=2834828');
        } catch (error) {
            console.error('An error occurred while initializing the TCF listener or player:', error);
        }
    })();
</script>
```

## RTB <a href="#rtb" id="rtb"></a>

For OpenRTB the TCF string can be sent via the Bid Request. The field depends on the ORTB version. See the [IAB Tech Lab OpenRTB GDPR advisory](https://iabtechlab.com/wp-content/uploads/2018/02/OpenRTB_Advisory_GDPR_2018-02.pdf) for details.

The Ad Server supports ORTB v2.4 and v2.5, which carry the GDPR and TCF information in the same fields:

* `regs.ext.gdpr`: `0/1`
* `user.ext.consent`: `CPAoZRHPArq3hBcADBENBJCgAAAAAAAAAAqIHKQAAOUgAAAA`

The Ad Server supports only TCF version 2 for RTB. A version 1 string throws an exception and the request is discarded.

The consent string can be decoded and validated with the [IAB TCF decoder](https://iabtcf.com/#/decode).


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.exads.com/general/iab-tcfv2.0/tcf-gdpr-compliant-for-ad-types.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
