# Welcome

Here you will find all the information you need to setup, customize and implement our AppConsent CMP solution. You can also contact support and view the FAQ.

***

## The basics of setting up your CMP

| What do you want to do?                                                                                 | Take action                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| I am just starting out and want to set up my first consent notice quickly.                              | [Set up my first notice](/appconsent-quick-start-for-a-web-notice) in a few minutes ⏲                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| I want to create and take the time to personalize my consent notice.                                    | [Create a step-by-step notice](/configuration/step-2-create-a-notice) 🖊                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| I want to implement my consent notice.                                                                  | Choose my environment: [Web](/configuration/step-3-notice-implementation-web-app-tv/web-cmp) / [Android App](/configuration/step-3-notice-implementation-web-app-tv/android) / [iOS App](/configuration/step-3-notice-implementation-web-app-tv/ios) / [React-Native](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk/react-native) / [Flutter](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk/flutter) / [Unity](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk/unity) |
| My audience must be able to change their choices at any time. How can I make it easy for them to do so? | [Display the privacy widget](/go-further/compliance/display-privacy-widget) on my platform 🏷                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| I want to track my performance statistics and the consent rate of my notice.                            | [Understand and use the dashboard](/go-further/statistics-and-a-b-test/understand-my-dashboard) 📊                                                                                                                                                                                                                                                                                                                                                                                                                                                      |

***

## Need help?

* [Support](/help/support)
* [FAQ](/help/faq)
* [Glossary](/help/glossary)

***

## AppConsent CMP in a few words

AppConsent is a CMP, a Consent Management Platform, created by SFBX. The GDPR aims to protect personal data by requiring any website or application to request the consent of users to collect and use their data. A CMP is a dedicated tool that allow websites, inApp applications to collect consent and disclose vendors informations as required by regulators.

If you or any third party vendor collect data for marketing purposes or other purposes at the id level (cookies, IDFA, ADID, Hashmail...), you need a CMP.

At SFBX, our main concern is to re-establish a relationship of trust between users and the various digital actors through reasoned and transparent use of personal data in order to offer them a personalized quality experience. Through the secure and transparent collection of consent allowing the processing of his data, the user can control its use.

Thanks to this regain of control, the user freely chooses to share his data in a relevant and unambiguous way.

Find out more about [AppConsent](https://sfbx.io/en/produits/) and our [plans](https://sfbx.io/en/pricing/).

Powered by [SFBX](https://sfbx.io/en/)&#x20;

<div align="left"><figure><img src="/files/iBdEqFsotIHuKEoPDbRo" alt="" width="375"><figcaption></figcaption></figure></div>


# AppConsent Quick start for a web notice

Find out how to create a consent notice and how to implement it on your website.

***

{% hint style="info" %}
**NOTE**

In this step-by-step guide, we explain how to create a basic notice using the *IAB Transparency & Consent Framework (TCF)* and then how to implement it on your website.

3 main steps:

1. Create a source
2. Create a notice
3. Insert your notice on your site

To discover other more advanced settings, for example, how to add your own partners (called extra vendors), we invite you to browse the summary of our documentation.
{% endhint %}

## 1. Create a source

* From the side menu, click on **Sources**, then **CREATE SOURCE.**

<figure><img src="/files/WoNHWKfsXcugbm0FuRfC" alt=""><figcaption></figcaption></figure>

* Choose **Website** in the type field.
* Enter the domain name of your website in the **website URL** field
* Choose your **IAB vendors** in the dedicated field

<figure><img src="/files/iRRZPWC4tVlqMFThIXyb" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
**TIP**

Thanks to your vendors selection, we make their IAB purposes appear in the dedicated field below. You still can edit this purposes & features list at your convenience.
{% endhint %}

* In **Purposes and Features** field, you can modify the automatic selection according to your needs.

If you can delete some purposes, note that by doing so you will automatically delete the associated vendors, as long as the vendors were only present for that goal. You cannot add a purpose if you did not select the associated vendors above.

You can also transform some purposes into a group, named Stack, if you need. If you know which stacks can match your data collection, we recommend you to select one or several stacks, your consent notice will be shorter.

<figure><img src="/files/MHNyGonMVA6Haekt4lfy" alt=""><figcaption></figcaption></figure>

Save the source

{% hint style="success" %}
**TIP**

Leave the other fields, for now, you can come back to them later.
{% endhint %}

## 2. Create a [notice](/help/glossary)

From the side menu, click on **Notices**, then **Create notice.**

<figure><img src="/files/zz8efeetsuZryzxXvmn6" alt=""><figcaption></figcaption></figure>

### 2.1. General Settings

Select in the **Source** field, the source previously created

<figure><img src="/files/jvlAueBKotmj6AaQQXRn" alt=""><figcaption></figcaption></figure>

### 2.2. Language & text customization

Select the language(s) you want in the **Language** field.

In the **Default language** field, set a default language in case it is not possible to determine the language of a user.

Add the link to your privacy policy for each of the languages selected above. This link will be automatically added to the **"See more paragraph" banner** field in the text customization section once the notice has been created.

{% hint style="success" %}
**TIP**

By clicking in the last field, you have the possibility to see the texts of the record. For the moment, we advise you to leave the default texts, and to come back to them later when you will have visualized your notice. In the next version of AppConsent, we will improve the way to modify the texts.
{% endhint %}

<figure><img src="/files/23MIMEI0VVQJJjFhNxEh" alt=""><figcaption></figcaption></figure>

### 2.3. Configuration

In the **Display layout** field, you have the choice between 2 layouts for your introduction page. By default, it is a central window. If you prefer a banner at the bottom of your site, select **Bottom full width**.

<figure><img src="/files/TjDN9wWdi52OlVI4auca" alt=""><figcaption></figcaption></figure>

<div><figure><img src="/files/M9BGKViH46zXDoMShvPk" alt=""><figcaption></figcaption></figure> <figure><img src="/files/7D3Gxd5OzJwRWUVtXW2b" alt=""><figcaption></figcaption></figure></div>

Choose the order of the buttons in the **Consent buttons** field. This is for the introduction page.

<figure><img src="/files/HnZff7T8SQAbzuldigDQ" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**INFO**

Compliance point (CNIL): since April 1st 2021, the French CNIL requires the presence of a button to  **refuse** the data collection.
{% endhint %}

Click on **save** at the end of the page.

That's it, your website's manual is created in AppConsent. Now it's time to integrate some pieces of code on your website. After that, your notice will be displayed on your website and the consent collection can start.

## 3. Integrate your notice on your site

{% hint style="warning" %}
**OTHER POSSIBLE IMPLEMENTATIONS**

You wish integrate AppConsent with Google Tag Manager ? See the dedicated page : [Install AppConsent with Google Tag Manager](/configuration/step-3-notice-implementation-web-app-tv/google-gtm)

You wish integrate AppConsent into shopify? See the dedicated page: [Implement AppConsent with Shopify](/configuration/step-3-notice-implementation-web-app-tv/web-cmp/implement-with-shopify)
{% endhint %}

***

1. Go to **Notices** tab in AppConsent configuration interface
2. Then under the notice you wish to integrate, copy the integration code using the "Copy" button

<figure><img src="/files/Bxu1gqaFs57Ysslc2O4b" alt=""><figcaption></figcaption></figure>

3. Finally, paste the code in the `<head>` tag in your website

{% hint style="warning" %}
Please add our CMP tag BEFORE any Google gtag() (GTM, GA and so on). Otherwise, you may experiment some discrepancies or warning by Google in you Google stack report.
{% endhint %}

That's it, your notice is now up and running on your website

***

{% hint style="info" %}
**TO GO FURTHER**

* Do you use Google Tag Manager? Find out how to integrate your notice with Google Tag Manager.
* Do you have partners that are not listed in the IAB? Find out how to add your own partners (called extra vendors)
* Are you collecting data for other purposes of your own? Find out how to add your own purposes (called extra purpose)
  {% endhint %}


# Step 1: Access AppConsent

***

## Create an account

[Link to create an account now](https://app.appconsent.io/#/register)

<figure><img src="/files/rCiv4jjn03FsVWXcK6a6" alt=""><figcaption></figcaption></figure>

For Essential and Standard plans:

You’ll be invited to complete some information about your profile (Company name, name and email).

After that, you will have 14 days to test one of the two AppConsent plans for free. You will be invited to complete your account information during this time. If you don’t complete your information, on the 15th day, you will need to do it and sign your contract before to access your account.

For Enterprise plan, [contact-us](mailto:support@sfbx.io)

***

## Switch plan or cancel subscription

From your account, you can manage your subscription: switch plan or cancel the current one.

### Switch Plan

Below your current plan's name, click on **Change Plan**.

<figure><img src="/files/llOD2FzagXxAFp2i01sA" alt=""><figcaption></figcaption></figure>

You will be redirected to the plan change page to select the plan that suits you.

For Enterprise plan, [contact-us](mailto:support@sfbx.io)

<figure><img src="/files/S1Lce9usIjX6NaWVly1F" alt=""><figcaption></figcaption></figure>

For **Standard plan**, you can modify the options: the additional number of unique monthly visitors and the additional number of domains

<figure><img src="/files/gw02cqszWuDV1tdDDZXn" alt=""><figcaption></figcaption></figure>

### Cancel Subscription

In the navigation bar, click on your username and choose **Billing**, you will be redirected to Stripe to cancel your subscription.


# Step 2: Create a notice

This section will help you to create your own notice, step by step, through AppConsent.

***

## What is a notice?

A notice is a consent window that you will implement on your website or your mobile App.

{% hint style="info" %}
**INFO**

The **Clear** notice template meets the accessibility requirements of **WCAG 2.1**, level **AA**. The tags required for assistive technologies (for screen reader software with Braille or audio output) have been implemented in the code. As for the graphic design, it was co-constructed with a UI designer specialized in accessibility issues. For example, the switches have two ways of signifying their status: color and icon (criterion *1.4.1 - Use of Color*)
{% endhint %}

### Web notice (Clear template)

<div><figure><img src="/files/KaVU9dXET1o91XAzZhNH" alt=""><figcaption></figcaption></figure> <figure><img src="/files/wPrdoeMYsocppljp5D3o" alt=""><figcaption></figcaption></figure></div>

### Mobile notice (Clear template)

Here is an example of a mobile notice, you can modify as you please during the creation.

<div><figure><img src="/files/O7x2VnyxCB8D8B1M9Tje" alt=""><figcaption></figcaption></figure> <figure><img src="/files/7VMSbO5OptqJPibACV35" alt=""><figcaption></figcaption></figure> <figure><img src="/files/XbGQ3G6Xzme4T4VYIv4d" alt=""><figcaption></figcaption></figure></div>


# 2.1. Create a source

***

#### Create a source for your website or your mobile app

Go to **Sources** on the left menu and click on **Create source**.

<figure><img src="/files/gd71j0bzHnGZghQBu9ke" alt=""><figcaption></figcaption></figure>

This is the source form :

<figure><img src="/files/p3oUB6AHwcJp3gM7v7SO" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/xnLqzEI6aTSkLeXggP7i" alt=""><figcaption></figcaption></figure>

## General information

* **Type:** Website, Unified SDK or Mobile App
* **Website URL or Application name:** Enter the URL of your website (can be either HTTP or HTTPS) or enter the name of your application (Unified SDK or Native SDK)
* **Notes**: Leave notes if you need them for your own usage

<figure><img src="/files/5MfRAQSyL3tuQ8UJsbdH" alt=""><figcaption></figcaption></figure>

## Vendors

* **IAB Vendors:** Select all the IAB vendors that are present on your website/ app from **Select** button
* **Extra vendors:** Choose the extra vendors you have created. You will also find a shortcut link "Create extra vendor" to create one at the bottom of the field or leave it blank if you don't have any.
* **Global extra vendors**: Choose any extra vendor from the [global extra vendors](/configuration/step-2-create-a-notice/2.1.-create-a-source/global-extra-vendors-global-extra-purposes) list provided by SFBX. These vendors are predefined and shared across all accounts. Their associated\
  purposes are automatically added to the source when selected.&#x20;

{% hint style="info" %}
**NOTE**

Utiq is an exception, it cannot be selected here and must be enabled from the "[Enable Utiq](#utiq-technology)" switch in the *Advanced Settings section*.
{% endhint %}

<figure><img src="/files/DjZ2PbNeJgzIXGLLdpnH" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/1ApRYu3VmuxTNsehRCTx" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
**TIP**

**Which vendors are present on my website?** To find out, use our Consent Guard tool, you will find all the trackers (cookies and other technical files) that are deposited on your website, and then come back to create your source.
{% endhint %}

{% hint style="info" %}
**NOTE**

**GVL:** It is updated automatically every Thursday night. In order to apply the new GVL in your notices, already created, you need to edit your sources, select all vendors, and save again the sources, don’t forget to save also the notices associated.
{% endhint %}

## Purposes & Features

{% hint style="info" %}
**NOTE**

Using AppConsent CMP, you can get the 12 IAB purposes. To guide you, purposes are automatically selected by us from your vendor's selection, but you can still delete some of them or transform them into stacks (defined group of purposes) that match your data collection and usage.

Concerning the stacks, the only legal rule is that you can't have the same purpose displayed two times for different usages, but our interface won't let this happen.
{% endhint %}

* **IAB purposes and features (Stacks):** you will automatically have purposes and features by selecting some vendors above, but you can still edit them.

  Eventually, you can delete some purposes from **Modify**. By doing so, note that you will automatically delete the associated vendors, as long as the vendors were only present for that goal.

  You can't add purpose if you don't have any vendors related to them. To do so, click on **Modify**.

{% hint style="info" %}
**NOTE**

Here is the list of purposes and features that are not editable once you have the vendors related.

* Purpose 1: Store and/or access information on a device

  Mandatory:
* Special purpose 1: Ensure security, prevent fraud and debug
* Special purpose 2: Technically deliver ads or content
* Feature 1: Match and combine offline data sources
* Feature 2: Link different devices
* Feature 3: Receive and use automatically-sent device characteristics for identification
  {% endhint %}

From **Modify**, you can also transform some purposes into stacks. On the right side, click on one or several stacks that match your way of collecting data. Automatically, purposes that composed that stack will be unselected. If you change your mind, just unselect stacks, and purposes associated will automatically be selected

{% hint style="success" %}
**TIP**

We recommend you to use stacks, it enables you to display less content on the main page of your consent notice, and that will be appreciable for your audience. When clicking on the stack, they will see all the purposes inside.
{% endhint %}

* **Extra purposes:** Choose the extra purpose you have created. You will also find a shortcut link **Create extra purpose** to create one at the bottom of the field or leave it blank if you don't have any.
* **Global extra purposes:** Choose any purpose from the global extra purposes list provided by SFBX. These purposes are predefined and shared across all accounts. They are automatically linked when you select the related global extra vendor.

{% hint style="info" %}
**NOTE**

Some of your privacy use-cases are not present in the IAB framework, that's why you can create Extra purpose to display them below the IAB purposes.

Some examples: geolocation purposes for mobile, newsletter...
{% endhint %}

* **\[For mobile app source only] Geolocation purposes:** A geolocation purpose is created like any other extra purpose, from the Source form, create your geolocation purpose directly with the short link **Create extra purpose** at the end of the Extra purpose field. It will be added automatically in the field.

Then, select them in the dedicated field: **Extra purpose: Geolocation for advertising purpose** and/or **Extra purpose: Geolocation for marketing purpose.**

Leave blank if you don't have any.

{% hint style="danger" %}
**ATTENTION**

For existing notices live in production, if you add or change the purposes linked to a source, you must regenerate the notice concerned by this source: just edit the notice you want and save it (you don't need to alter anything). A new version of the CMP will be pushed to production.
{% endhint %}

For the modalities of displaying the geolocation purposes, go to the right section for Android and for iOS.

* **Enable only consent as the legal basis for processing:** if the option is activated, only the purposes falling under the legal basis of consent are taken into account in the collection. Purposes falling under the strict legitimate interest (with right of opposition) are still visible on the notice but won't be collected.

{% hint style="danger" %}
**CAUTION**

Legitimate interest is one of the actionable legal bases that vendors may want to leverage in order to process your data and the data of your visitors.

In short terms:

* With consent: Without a YES it's a NO,
* With legitimate interest: Without a NO it's a YES.

This legal base is clearly disputed for marketing purposes and moreover, it' not possible to use this legal base in order to access or drop cookies in almost all EU countries.
{% endhint %}

{% hint style="info" %}
**NOTE ABOUT FLEXIBLE LEGAL BASES**

In the IAB TCF V2, vendors can declare Flexible purposes. That means that they can operate data on both legal base signals.

When you activate **Enable only consent as the legal basis for processing**, here are the underlying things that happen:

**Case 1**: Vendor under full consent - No change

**Case 2**: Vendor with some purposes in Legitimate interest This vendor will lose the right to operate under legitimate interest for selected purposes.

**Case 3**: Vendor with some purposes in Legitimate interest and Flexibles Purposes. This vendor will see his flexible purposes applied in consent and not a legitimate interest. This vendor will lose the right to operate under legitimate interest for selected purposes.
{% endhint %}

<figure><img src="/files/mbxMkrTPn29KhRlYabh0" alt=""><figcaption></figcaption></figure>

* **Link your add-ons**: If you have some extra vendors, you can link them to purpose 1 (IAB) or any other extra purpose that you have created. To do so, click on **Choose purpose**.

If you have some extra purposes, you can link them to any IAB vendors that you have previously selected or extra vendors that you have created. To do so, click on **Choose vendor**.

<figure><img src="/files/FJBgCR3NNizr8L8lChDe" alt=""><figcaption></figcaption></figure>

* **Advanced Settings** : Several advanced settings are available.

## Advanced Settings

### **Automatic deletion of cookies**

<figure><img src="/files/J2Z03b3rkPRtZaEiWkth" alt=""><figcaption></figcaption></figure>

#### Enable automatic cookies deletion after withdrawal of consent (switch)

When enabled, all 1st party cookies are deleted upon withdrawal of consent, except those you explicitly list to be kept.

Turn this option on to activate the deletion, then use the Cookies to keep field below to define the exceptions.

#### Cookies to keep after withdrawal of consent

This option lets you list the cookies that must be retained after a user withdraws consent (for example, technical or session cookies you need to preserve).

**How to fill in this field ?** Enter one cookie name per line, press `Enter` to start a new line for each additional cookie. Each line is matched independently, and the field supports regular expressions.

Example:\
\_mycookie\
cookie.\*

When enabled, a 1st party UUID will be generated and stored in a cookie to identify users across subdomains. You **must** edit the text field below the switch to define the subdomain.

<figure><img src="/files/CIVnAOtpvuW2cx1JbPiy" alt=""><figcaption></figcaption></figure>

### Excluded URLs from display

<figure><img src="/files/SLX1U8o4eO6xRLOcLvDq" alt=""><figcaption></figcaption></figure>

This option allows you to specify URLs on which the CMP will not be displayed. This is useful for pages such as privacy pages.

**How to fill in this field ?** Enter one URL per line, press `Enter` to start a new line for each additional URL. Each line is matched independently, and the field supports regular expressions.

### Utiq technology

Utiq is a telecom-operator-powered marketing identifier technology. When enabled on a source, the CMP declares a dedicated Utiq vendor and purpose, displays a Utiq paragraph in layer 1 of the consent banner, and loads the Utiq script from a customer subdomain (`utiq.<domain>` CNAME).

For more details on the Utiq integration, see the [Utiq integration page](/configuration/step-3-notice-implementation-web-app-tv/web-cmp/utiq-integration).

<figure><img src="/files/0zirf81tKgG03DPz2D8g" alt=""><figcaption></figcaption></figure>

## Migrating an App source to Unified SDK

The Duplicate & migrate to Unified SDK option is only available for sources of type App.

The migration is non-destructive: your original source and its notices are never changed. A copy of the source and of all its active notices is created and converted to the Unified SDK.

{% hint style="info" %}
A few settings are adapted automatically, because App and Unified SDK notices don't share the exact same options:

* the App onboarding image is reused as the notice icon
* the "Accept / Understand" banner action (App-only) is replaced by the default action
* App-only settings with no Unified SDK equivalent (e.g. the "Accept all" button highlight, rollback availability, several App-specific colors, the geo-advertising icon) are removed.

All other settings (texts, translations, colors, images, vendors, purposes and stacks) are kept unchanged.
{% endhint %}

## Save the source

{% hint style="success" %}
Confirm by clicking on **Save.**
{% endhint %}

Your source now appears on the board:

<figure><img src="/files/KalLYbKDn8MMA8bLcVvJ" alt=""><figcaption></figcaption></figure>

* **ID:** the unique ID of the source.
* **Target URL or App ID:** the name of the source
* **Creation:** Time elapsed since the creation of the notice.
* **Last Update:** Time elapsed since the last update of the notice.
* **Status:** For now only one status: active.

## Edit your source

You can edit your source at any time. Just go back to the **Sources** menu and click on the row of your choice. The page will appear and you can update your source information.

{% hint style="danger" %}
**CAUTION**

To make the changes appear in the notices, you must save all the notices concerned. Just edit the notice and save it (you don't need to alter anything). A new version of the CMP will be pushed to production.
{% endhint %}


# Add extra vendor

***

## Add extra vendor

First, click on [**Extra Vendors**](/help/glossary#extra-vendor)**.** Then, **Add extra vendor**

<figure><img src="/files/GxapTPbDLw3lfuDz3QCk" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/SPcF7QgcTvXOU2KFLbrg" alt=""><figcaption></figcaption></figure>

You should have this:

<figure><img src="/files/lQ2xxqxeDuQogRNY9Ug6" alt=""><figcaption></figcaption></figure>

Now that you have added your extra vendors, you can [add them to your source](/configuration/step-2-create-a-notice/2.1.-create-a-source#vendors).

## Extra vendors in your notice

The extra vendors will be displayed in a list, and the consent button will be displayed for each of them.

**For a web notice**: (Clear template)

<div align="left"><figure><img src="/files/ciyFY5qq5CGBNulUgzKt" alt="" width="563"><figcaption></figcaption></figure></div>

**For a mobile App (iOS or Android)**: (Clear template)

<div align="left"><figure><img src="/files/Xq49P4PsWq5tS8GEOYb6" alt="" width="264"><figcaption></figcaption></figure></div>

The extra vendors will be also displayed below the purpose you assign them.


# Add extra purpose

***

From the menu [**Extra purpose**](/help/glossary#extra-purpose), click on **Add Extra purpose**

<figure><img src="/files/Xm8iCchnrhJ4aEA2JXtg" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/hXyxu06FXMSoIJr7caiW" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/w0kyGl1la3AZLtAAa31b" alt=""><figcaption></figcaption></figure>

In **Type** field: select between a Fixed [extra-purpose](/help/glossary#extra-purpose) or a [Floating](/help/glossary#floating-purposes) one

* **Fixed** means that they will appear in the display of your notice.
* \[AppConsent Premium] **Floating** means that we do collect consent, but the display of the request is up to you.

Click **Save and Add** if you want to save and create another purpose. Otherwise, click **Save**.

For each language, you select, fill in an extra purpose name and a description carefully, as these are the words that will be exposed to your users to get consent for this purpose.

{% hint style="info" %}
As an identifier, a Slug is created using the purpose name and the default language. It's unique across your account. It will be useful to operate **Source** settings.
{% endhint %}

You can edit your extra purpose at any time. Just go back to [**Extra purpose**](/help/glossary#extra-purpose) in the menu and click on the desiderated line. The popup will appear and you can update the information. Then, go into the **Notices** section, edit the notice form and save it again, to make sure the update is ok.

Once your extra purpose is created, you need to link it to at least 1 IAB vendor or Extra vendor, from the **Link your add-ons** section in the source creation form.

{% hint style="danger" %}
**CAUTION**

You can edit and change the purposes linked to a source whenever you need, but in this case, you must regenerate the notice concerned by this source: just edit the notice you want and save it (you don't need to alter anything). A new version of the CMP will be pushed to production.
{% endhint %}


# Global extra vendors / Global extra purposes

## Global extra vendors

A **Global extra vendor** is an extra vendor that we predefine and make available to all accounts. Unlike a [regular extra vendor,](/configuration/step-2-create-a-notice/2.1.-create-a-source/add-extra-vendor) you don't create or configure it yourself, we manage it for you (name, logo, privacy policy URL, linked purposes, etc.)

On the **Extra Vendors page**, a dedicated section called *"List of global extra vendors"* lists every global extra vendors available, next to your own extra vendors.

<figure><img src="/files/YLcr4jAETJD5gdRGw1It" alt=""><figcaption></figcaption></figure>

You can click a row to see its details (name, logo & privacy policy URL).

<figure><img src="/files/REjYfdwGE7srGXsCgz0g" alt=""><figcaption></figcaption></figure>

When you [**create a** **source**](/configuration/step-2-create-a-notice/2.1.-create-a-source), you can select a global extra vendor just like any other extra vendor. Once selected, its associated purposes (IAB or global extra) are automatically linked to the source.

{% hint style="info" %}
**NOTE**

**Exception for Utiq**: Utiq is not selectable from the vendors list. To add it to a source, you must enable the *"Enable Utiq"* switch in the *Advanced Settings* section. The Utiq vendor and its purpose are then added to the source automatically.
{% endhint %}

***

## Global extra purposes

Alongside global extra vendors, we also provide **global extra purposes**. They are predefined and managed by SFBX. You cannot create, edit, or delete them.

You can see the full list of available global extra purposes on the **Extra Purposes page**, in a dedicated section called *"List of global extra purposes"*.&#x20;

<figure><img src="/files/djTiztIJX1FWkTKDncz2" alt=""><figcaption></figcaption></figure>

As with vendors, you can click a row to see the details (the shortID and the purpose name & description in every available language), but everything is read-only.

<figure><img src="/files/jkIkoesosArihhoCb26o" alt=""><figcaption></figcaption></figure>

A global extra purpose can be linked to a global extra vendor, but it can also exist on its own and be selected independently on a source.&#x20;

In the source creation form, when a global extra purpose is linked to a global extra vendor, it is automatically added to the source as soon as you select that vendor. When it is independent, you can select it manually like any other extra purpose.

{% hint style="info" %}
**NOTE**

**Exception for Utiq**: the Utiq purpose is tied to the Utiq vendor and follows the same rule. It is not selectable from the list, it is added automatically when the *"Enable Utiq"* switch is turned on in the *Advanced Settings* section of a source.
{% endhint %}


# 2.2 Add a notice

{% content-ref url="/pages/iHE2eTe4WXFJRDhHy8DP" %}
[Configure a web notice](/configuration/step-2-create-a-notice/2.2-add-a-notice/configure-a-web-notice)
{% endcontent-ref %}

{% content-ref url="/pages/k43ms8nQaHoOMTcXPX7r" %}
[Configure a mobile notice](/configuration/step-2-create-a-notice/2.2-add-a-notice/configure-a-mobile-notice)
{% endcontent-ref %}

{% content-ref url="/pages/4aeyhv7NyGoV6MbI0ppW" %}
[2.3 Actions on a notice](/configuration/step-2-create-a-notice/2.3-actions-on-a-notice)
{% endcontent-ref %}


# Configure a web notice

***

{% hint style="info" %}
Before creating your notice, you need to have a valid source declared in AppConsent.&#x20;

See section [**Create a source**](/configuration/step-2-create-a-notice/2.1.-create-a-source) in order to learn how to create a new source
{% endhint %}

In the left menu, click on **Notices** and then on **CREATE NOTICE**.

<figure><img src="/files/Asfyr9yGezonTtbbxtH0" alt=""><figcaption></figcaption></figure>

## **Settings**

* **Source**: Select the source you want to attach the notice to. Notice is always linked to one source.
* **Notes**: Add the comments you want. It’s a reminder for your own usage and it will not be displayed anywhere in the notice.

<figure><img src="/files/6z03d7mO9zWUB3nX1z97" alt=""><figcaption></figcaption></figure>

## **Language & text customization**

<figure><img src="/files/d9Jjxh4HoaUGbJhUsV9I" alt=""><figcaption></figcaption></figure>

### Languages

Select the language you want to be included in your notice. Languages available for web notice:

* English
* French
* Spanish
* Italian
* German
* Dutch
* Portuguese
* Polish
* Bulgarian
* Czech
* Catalan
* Swedish
* Danish
* and 36 other languages available

### Default language

This language will be used if the browser language is not configured in one of the languages included in the notice.

By default, a new notice is configured in English only. But you can add other languages and translations.

### Privacy policy URL <a href="#privacy-policy-url" id="privacy-policy-url"></a>

<figure><img src="/files/JSaYzS1JAdGbHP9yNSXP" alt=""><figcaption></figcaption></figure>

Add the link to your privacy policy for each of the languages selected above. This link will be automatically added to the **"See more paragraph" banner** field in the text customization section once the notice has been created.

### Text customization

<figure><img src="/files/GQZIhBO3qb2wuCNPoM1C" alt=""><figcaption></figcaption></figure>

In each language tab, you will find all text of your notice. You can modify them. But you can also reset the default texts by clicking on **Reset to default**.

{% hint style="warning" %}
**IMPORTANT**

Please note that the default text of the banner **banner\_moreDetails\_paragraphs** field does not mention your link to your privacy policy page.&#x20;

If the `<privacy_policy_url>` tag is not present in the **"See more paragraph" banner** field, be sure to add your own link by completing the `<a\>` tag.
{% endhint %}

It is possible to create links in text to redirect users to other parts of the notice. For this, two tags are available:

* **\<goto\_vendors>**: Allows to create a redirection link to the list of all partners (vendors) that are present in the notice.
  * Note: The **\<goto>** tag is deprecated, use **\<goto\_vendors>** instead
* **\<goto\_settings>**: Allows to create a redirection link to consent settings page

The titles of the IAB purposes and their definitions can't be changed. It's due to the IAB TCF compatibility with their Term & Privacy policies.

## **Banner layout**

### Display layout

Choose between a bottom banner or a middle modal window.

### Consent buttons

* Choose the buttons and their order on the introduction page
* Choose to add or not the "Continue without accepting" button so that the user can leave the notice without making a choice. By default, it is a link "continue without accepting" placed at the top right of the window.

{% hint style="info" %}
**NOTE**

From April 1st, 2021, the "Refuse All" button is mandatory in France. To learn more about the new guidelines of the CNIL since April 1st, you can have a look at our [dedicated article](https://sfbx.io/2021/03/21/la-cnil-renforce-ses-lignes-directrices-sur-lusage-des-cookies-mais-sfbx-avait-deja-pense-a-tout-ou-presque/)
{% endhint %}

#### Continue without accepting & consent buttons

The Continue without accepting button and the banner action (the set of consent buttons shown on your banner) are linked.

When Continue without accepting is enabled, only the banner layouts without a Deny button are allowed (Accept / Configure and Configure / Accept). All layouts that include a Deny button are disabled in the selector.

<figure><img src="/files/m1M7cfHHrPgUKwo9G5Bn" alt=""><figcaption></figcaption></figure>

### Illustration

<figure><img src="/files/FnYTlgUOgjAbNWJP8yQZ" alt=""><figcaption></figcaption></figure>

### Vertical buttons

The Vertical buttons option lets you stack your consent buttons (Accept, Refuse, Configure…) on top of each other instead of displaying them side by side. The option works on both modal banners and bottom banners.

When your notice uses a bottom banner and Vertical buttons is enabled, an extra setting appears: Vertical buttons position for bottom banner.

<figure><img src="/files/C7SlI5QuuciN9lXPtUkM" alt=""><figcaption></figcaption></figure>

### Illustration

Choose to display illustrations or not on your banner. (only available for Clear banner). The default image is the one provided by SFBX, but you can customize it using the three upload fields below the activation switch.

<figure><img src="/files/YwK9zfIqavD1OMXotfxG" alt=""><figcaption></figcaption></figure>

### Success Screen

<figure><img src="/files/2OORE0ausDFFGyMfIjLM" alt=""><figcaption></figcaption></figure>

Activate this option if you want the following screen when the user goes beyond the introduction screen and makes mixed choices.&#x20;

<div align="left"><figure><img src="/files/OM6ZAl1eH6ALOko3GY1I" alt="" width="375"><figcaption></figcaption></figure></div>

**Customise the success screen image that will be displayed on your banner** : If you have chosen to display a success screen, choose the image to be displayed on it.

### Display purposes list on layer 1

<figure><img src="/files/o4xFaZDQohJHV4ag3MtR" alt=""><figcaption></figcaption></figure>

*\[ Standard, Essential & Premium Plans]*&#x20;

Choose to display or hide the list of processing purposes used by you and your partners on the main page of the banner.

{% hint style="danger" %}
**IMPORTANT**

Please note that the IAB TCF (Transparency and Consent Framework) and the various European DPAs require the purposes of processing to be displayed. If you deactivate this option, it is your responsibility to inform your users in an appropriate manner about the purposes for which their personal data is processed.
{% endhint %}

#### Purposes list display mode

When the purposes list is displayed, you can choose how it appears on the main page of the banner.

* Display purposes list as a dedicated list (default): the processing purposes are shown as a separate, structured list on the banner's main page.
* Display purposes list within the banner text: the processing purposes are listed inline, directly inside the banner's introductory paragraph.

### Privacy widget

This option enables you to activate the privacy widget and configure its display (colour, position and text). [**More information**](/go-further/compliance/display-privacy-widget)

### Url redirect

The CMP offers the option of defining redirection urls after the click for each consent button ("refuse all", "accept all" and "continue without accepting") on each page (main and settings).

<figure><img src="/files/19bMUakT5d6vbp2H6kqS" alt=""><figcaption></figcaption></figure>

## **UI customization**

*\[ Standard, Essential & Premium Plans ]*

<figure><img src="/files/wHdm2fOZqYL01cXn9wJo" alt=""><figcaption></figcaption></figure>

### Banner pictures

* **Add a logo or icon** : Your image will be displayed on your banner

When you add a logo to your banner, you can now choose how the text is arranged around it.&#x20;

By default, the logo and the banner title are aligned side by side. You can also enable a new text-wrap layout: the text flows around the logo instead of staying beside it, so more content fits in the same space.

{% hint style="info" %}
This layout option is available on the Clear CMP only.
{% endhint %}

### Buttons and texts color

<figure><img src="/files/G2zXoSCciesYXv2vGUkN" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**NOTE**

If you do not fill any color codes, then the default colors will be applied.
{% endhint %}

#### **Header and background**

{% tabs %}
{% tab title="Clear template" %}

<figure><img src="/files/0IzsOB9vrEYWVIkeHB2G" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/tykT4fXE4V4vISkF98kR" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Classic template" %}

<figure><img src="/files/nwqZdWUqDN52F6pHuyjw" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

#### **Consent buttons**

{% tabs %}
{% tab title="Clear template" %}

<figure><img src="/files/BHCgnU62a2Qe9WjLp2Bm" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Classic template" %}

<figure><img src="/files/cGTBeDfsddg3wKOMZiDo" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

{% hint style="danger" %}
**ATTENTION**

Following the latest declarations of the CNIL, we insist that you do not favor one CTA over another. The European regulator is particularly attentive to this subject. For example, if you change the style ( background, size...) of an Accept button, you must apply the same style to the **Configure** or **Reject** button.
{% endhint %}

#### **Switches**

{% tabs %}
{% tab title="Clear template" %}

<figure><img src="/files/qmleiqhmcfYyJvpShvod" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Classic template" %}

<figure><img src="/files/moFCtmUYC1Pa2tgDMyY5" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

### Custom CSS

<figure><img src="/files/GppuckAauv8wijDIEiqC" alt=""><figcaption></figcaption></figure>

#### Additional CSS code

<details>

<summary>Example of full CSS customization for Classic banner</summary>

```css
/* ---- Modal container ---- */
  .banner--modal {
  background: linear-gradient(135deg, #dfe4ff 0%, #e9d8fd 50%, #ffd6e8 100%) !important;
  border: solid 1px #fff !important;
}

/* ---- Description text ---- */
.banner__mainContent {
  background: rgba(255, 255, 255, 0.45) !important;
  border-left: 3px solid #6c5ce7 !important;
  border-radius: 10px !important;
  padding: 14px 18px !important;
}

/* ---- Buttons ---- */
.button {
  border-radius: 12px !important;
  font-weight: 600 !important;
  color: #6c5ce7 !important;
}
.button:hover {
  cursor: pointer;
}
.button span {
  color: #6c5ce7 !important;
}

/* ---- Link ---- */
a {
  color: #6c5ce7 !important;
}

/* ---- Button accept all ---- */
.button__acceptAll {
  background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%) !important;
  border-color: rgb(162, 155, 254, 0.8) !important;
  color: #ffffff !important;
  box-shadow: 0 8px 20px -6px rgba(108, 92, 231, 0.6) !important;
}
.button__acceptAll span {
  color: #ffffff !important;
}
.button__acceptAll:hover {
  box-shadow: 0 12px 26px -6px rgba(108, 92, 231, 0.75) !important;
}

/* ---- Button refuse all ---- */
.button__refuseAll {
  background: transparent !important;
  border-color: rgba(108, 92, 231, 0.8) !important;
  color: #6c5ce7 !important;
  box-shadow: none !important;
}
.button__refuseAll:hover {
  background: rgba(108, 92, 231, 0.08) !important;
  border-color: #6c5ce7 !important;
  box-shadow: 0 6px 16px -8px rgba(108, 92, 231, 0.5) !important;
}

/* ---- Button set up ---- */
.button__openPrivacyCenter,
.button__openPrivacyCenter:hover {
  background: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
  transform: none !important;
}
.button__openPrivacyCenter {
  position: relative !important;
  color: rgb(108, 92, 231) !important;
}
.button__openPrivacyCenter::after {
  content: "" !important;
  position: absolute !important;
  left: 50%; bottom: 6px !important;
  width: 0; height: 2px !important;
  background: #6c5ce7 !important;
  transition: width 0.25s ease, left 0.25s ease !important;
}
.button__openPrivacyCenter:hover::after {
  width: 60% !important;
  left: 20% !important;
}
```

</details>

<details>

<summary>Example of full CSS customization for Clear banner</summary>

```css
.modal__container {
  background: linear-gradient(135deg, #dfe4ff 0%, #e9d8fd 50%, #ffd6e8
  100%) !important;
  border-radius: 18px !important;
  border: solid 1px #fff !important
}

.title {
  background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%) !important;
  -webkit-background-clip: text !important;
  background-clip: text !important;
  -webkit-text-fill-color: transparent !important;
  font-weight: 700 !important;
  letter-spacing: -0.01em !important;
}

/* ---- Buttons ---- */
.button {
  border-radius: 12px !important;
  font-weight: 600 !important;
  color: #6c5ce7 !important;
}
.button:hover {
  cursor: pointer;
}

/* ---- Button accept all ---- */
.button__acceptAll {
  background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%)
  !important;
  border-color: rgb(162, 155, 254, 0.8) !important;
  color: #ffffff !important;
  box-shadow: 0 8px 20px -6px rgba(108, 92, 231, 0.6) !important;
}
.button__acceptAll:hover {
  box-shadow: 0 12px 26px -6px rgba(108, 92, 231, 0.75) !important;
}

/* ---- Button refuse all ---- */
.button__refuseAll {
  background: transparent !important;
  border-color: rgba(108, 92, 231, 0.8) !important;
  color: #6c5ce7 !important;
  box-shadow: none !important;
}
.button__refuseAll:hover {
  background: rgba(108, 92, 231, 0.08) !important;
  border-color: #6c5ce7 !important;
  box-shadow: 0 6px 16px -8px rgba(108, 92, 231, 0.5) !important;
}

/* ---- Button set up ---- */
.button__openPrivacyCenter,
.button__openPrivacyCenter:hover {
  background: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
  transform: none !important;
}
.button__openPrivacyCenter {
  position: relative !important;
  color: rgb(108, 92, 231) !important
}
.button__openPrivacyCenter::after {
  content: "" !important;
  position: absolute !important;
  left: 50%; bottom: 6px !important;
  width: 0; height: 2px !important;
  background: #6c5ce7 !important;
  transition: width 0.25s ease, left 0.25s ease !important;
}
.button__openPrivacyCenter:hover::after {
  width: 60% !important;
  left: 20% !important;
}

```

</details>

{% hint style="info" %}
**NOTE**&#x20;

**CSS** : You can past specific CSS rules to customize the UI and adapt it. You can change the background color and so on.&#x20;

Example:

`.banner--modal {`&#x20;

`background: white!important;`&#x20;

`color: #656565!important;`

`}`&#x20;

`.title {`&#x20;

`color: #656565!important;`

`}`

The CSS rules are common for each languages.
{% endhint %}

#### Additional CSS code for AMP&#x20;

If you're using AMP, put in this field the same code as above.

{% hint style="info" %}
**INFO**

If Additional CSS code for AMP is empty, the classic CSS will be applied to all notices, including AMP.

If Additional CSS code for AMP is filled in, it will be applied to AMP notices, the classic CSS will be applied to other notices.
{% endhint %}

## **Google Consent Mode (GCM) settings**

Enable GCM (Google Consent Mode) so that Google Analytics is controlled by the CMP. See the [**AppConsent configuration for Google Analytics**](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/manage-google-analytics) section.

<figure><img src="/files/CoYFqlBdkeubpzrPKfLN" alt=""><figcaption></figcaption></figure>

### Enable TCF integration with GCM

Allows Google to use the IAB Transparency & Consent Framework to send consent signals to Google tags. [**Learn more**](https://developers.google.com/tag-platform/security/guides/implement-TCF-strings)

### Enable GCM Basic Consent Mode

Delay the loading of Google tags until the user interacts with the notice, thereby regulating data transmission. [**Learn more**](https://support.google.com/google-ads/answer/10000067?hl=en\&sjid=11442004191081153688-EU).

### Countries to exclude

Allows you to define a list of countries in which Google Consent Mode will not be applied.

### Url\_passthrough

When this property is activated, Google can transmit information in the URL about ad clicks. [**Learn more**](https://developers.google.com/tag-platform/security/guides/consent?hl=en\&consentmode=advanced#passthroughs).

### Ads\_data\_redaction

Used to hide ad data when ad\_storage is set to **denied**. [**Learn more**](https://developers.google.com/tag-platform/security/guides/consent?hl=en\&consentmode=advanced#redact_ads_data).

## **Advanced settings**

<figure><img src="/files/fPZwb4C5xLSxWqhiAFeg" alt=""><figcaption></figcaption></figure>

### ForceGDPRApplies

Enables you to force the notice to be displayed for all visitors from any country in the world.

### GDPR extra countries

When this property is configured, the notice will only be displayed for the list of countries selected, in addition to countries already displayed according to the rules defined by the RGPD.

### Floating extra purpose

A floating purpose is a purpose for which only the storage of the consent is concerned. See the **Floating purpose feature** section.

{% hint style="info" %}
**NOTE**

The text part is managed on your side. In the case of Floating, it is a new API save-floating-purpose route that receives them with the UUID, the AppKey of the notice as well as the possible External IDs. The consent payload is stored separately in another table. If the payload contains an external ID it must be saved as it is at the moment in order not to lose the correspondence UUID/ContractID etc.
{% endhint %}

### Enable Legitimate Interest purposes when Refuse All/Continue without accepting (skip)

If the user refuses or leaves, you can decide to collect any data (switch off) or collect only data for purposes under a legitimate interest legal base (switch on)

{% hint style="info" %}
**NOTE**

As recommended by the Italian regulator, the Garante, our **Continue without accepting** link is automatically replaced by a cross for all web notices, for whom the user browser is in Italian. This cross has the same effect as our link Continue without accepting: close the notice with no consent.
{% endhint %}

### Excluded URLs from display

You can prevent the notice from being displayed by entering either a specific URL or a regular expression matching the pages to exclude. For example, for a Privacy Policy page, you can provide its full URL directly or define a regex targeting its path. Any URL matching the entered value will not trigger the display of the notice.

### Consent retention period

These default values, which we recommend, allow you to be in compliance while maximizing your consent rates. Those durations represent the time lapse before the consent notice will be displayed again and consent will be sought once more.

<figure><img src="/files/DpCbLKsIp43gamQy6r0T" alt=""><figcaption></figcaption></figure>

* Accept all: **12 months**
* Refuse all: **6 months**
* Mixed choice: **12 months**
* (If applicable) Continue without accepting: **3 days**

You can modify the durations at any time.

{% hint style="info" %}
If you have created your consent notice before October 28th, 2021, the default settings for mobile consent notices are on 12 months for all choices. Feel free now to adjust them.
{% endhint %}

## Save the notice

{% hint style="success" %}
Then click on **Save**. The notice in Production is updated and ready to be implemented in your application.
{% endhint %}

Each update is stored in a specific ledger in our blockchain stack. We store the whole notice and the text altered, precise timestamp, account\_id, consoleUserID, and so on.


# Configure a mobile notice

***

{% hint style="info" %}
**NOTE**

Before creating your notice, you need to have a valid source declared in AppConsent.  This source must have the Application type and an Application name.

See section [**Create a source**](/configuration/step-2-create-a-notice/2.1.-create-a-source) in order to learn how to create a new source.&#x20;
{% endhint %}

In the left menu, click on **Notices** and then on **CREATE NOTICE**.

<figure><img src="/files/iDTqe7QDCbEthbNwuUjJ" alt=""><figcaption></figcaption></figure>

## **Settings**

* **Sources:** Select the source you want to attach the notice to. A notice is always linked to one source and for your App, this source needs to have a **Mobile App** type and a **Name of application**.
* **Notes:** Add the comments you want. It’s a reminder for your own usage and it will not be displayed anywhere in the notice.

<figure><img src="/files/F0XDjpqNPf0b3CtsqauI" alt=""><figcaption></figcaption></figure>

## **Language & text customization**

<figure><img src="/files/JC7zxGEBEsuKxrJXYJ31" alt=""><figcaption></figcaption></figure>

### Languages

Select the language you want to be included in your notice. Languages are available:&#x20;

* English
* French
* Spanish
* Italian
* German
* Dutch&#x20;
* Portuguese
* Polish
* Bulgarian
* Czech
* and 36 other languages available

### Default language

This language will be used if the browser language is not configured in one of the languages included in the notice.

By default, a new notice is configured in English only. But you can add other languages and translations.

### Privacy policy URL <a href="#privacy-policy-url" id="privacy-policy-url"></a>

<figure><img src="/files/dwkESiSaXb2jrKSuFBkf" alt=""><figcaption></figcaption></figure>

Add the link to your privacy policy for each of the languages selected above. This link will be automatically added to the **"See more paragraph" banner** field in the text customization section once the notice has been created.

### Text customization

In each language tab, you will find all text of your notice. You can modify them. But you can also reset the default texts by clicking on **Reset to default**.

{% hint style="warning" %}
**IMPORTANT**

Please note that the default text of the banner **banner\_moreDetails\_paragraphs** field does not mention your link to your privacy policy page.&#x20;

If the `<privacy_policy_url>` tag is not present in the **"See more paragraph" banner** field, be sure to add your own link by completing the `<a\>` tag.
{% endhint %}

#### **Add clickable link in your texts for Android and iOS**

If you need to add clickable links in your text, simply place a URL in your text using `<a href="insert your link here">privacy policy</a>` tag to have clickable text that will redirect the user to a URL.

It is possible to create links in text to redirect users to other parts of the notice. For this, two tags are available:

* **\<goto\_vendors>**: Allows to create a redirection link to the list of all partners (vendors) that are present in the notice.
* Note: The **\<goto>** tag is deprecated, use **\<goto\_vendors>** instead
* **\<goto\_settings>**: Allows to create a redirection link to consent settings page

The titles of the IAB purposes and their definitions can't be changed. It's due to the IAB TCF compatibility with their Term & Privacy policies.

<div><figure><img src="/files/IEic1UvrXANZ2Q6zHQ4h" alt="" width="563"><figcaption><p>Introduction page</p></figcaption></figure> <figure><img src="/files/H6fqkf2YYYp989lLrDll" alt="" width="563"><figcaption><p>Main page</p></figcaption></figure> <figure><img src="/files/2nALfOzlSmgGpBMyMKCQ" alt="" width="563"><figcaption><p>Main page</p></figcaption></figure></div>

<div><figure><img src="/files/YTYP913HH074ZnZ1frru" alt="" width="375"><figcaption><p>Purpose page</p></figcaption></figure> <figure><img src="/files/SfFUFT7Pu1rSjQ6a4sRD" alt="" width="401"><figcaption><p>Mandatories purpose page</p></figcaption></figure> <figure><img src="/files/jlIyglawvppkSRBopTHQ" alt=""><figcaption><p>Mandatories features page</p></figcaption></figure></div>

<div><figure><img src="/files/9hmBDepg3r8AE4uhhxKn" alt="" width="540"><figcaption><p>Refine by partners page</p></figcaption></figure> <figure><img src="/files/qNc9tRA6BMfxxWHXajPU" alt="" width="540"><figcaption><p>Vendor page</p></figcaption></figure> <figure><img src="/files/IpgoxDgKCMJnHbfoCDHl" alt="" width="540"><figcaption><p>Vendor page</p></figcaption></figure></div>

<div align="left"><figure><img src="/files/f97uOpH6CrpCFUi1s7Ja" alt="" width="180"><figcaption><p>Success page</p></figcaption></figure></div>

{% hint style="info" %}
**NOTE**

There is no need to change the text in the fields below, these entries will not be taken into account:

* Seemore
* privacy\_sections\_stacks
* choices
* consentable\_vendors\_popover\_title
* consentable\_legintvendors\_popover\_title
* consentable\_legint\_accept
* vendor\_legints\_info
* mandatories\_feature\_section
* mandatories\_purpose\_section
* banner\_moreDetails\_open
* banner\_moreDetails\_close
* stack\_details\_switch\_title
  {% endhint %}

## **Banner layout**

<figure><img src="/files/tGbcEB1KlNOovR1OKW0C" alt=""><figcaption></figcaption></figure>

### Consent buttons

* Choose the buttons and their order on the introduction page
* \[3 buttons] Choose to highlight only the **Accept** button. If activated, the Accept button will be more prominent than the other two buttons.
* Choose to add or not the "Continue without accepting" button so that the user can leave the notice without making a choice. By default, it is a link "continue without accepting" placed at the top right of the window.

{% hint style="info" %}
**NOTE ABOUT ITALIAN NOTICES**

As recommended by the Italian regulator, the Garante, our **Continue without accepting** link is automatically replaced by a cross for all InApp notices, for whom the user device has an Italian country code. This cross has the same effect as our link **Continue without accepting**: close the notice with no consent.
{% endhint %}

#### Continue without accepting & consent buttons

The Continue without accepting button and the banner action (the set of consent buttons shown on your banner) are linked.

When Continue without accepting is enabled, only the banner layouts without a Deny button are allowed (Accept / Configure, Configure / Accept and Accept / Understand). All layouts that include a Deny button are disabled in the selector.

<figure><img src="/files/bTMxHeMSc3vRD5bVwQV2" alt=""><figcaption></figcaption></figure>

### iOS ATT

<figure><img src="/files/dYwGFII89XsFmsjsdBie" alt=""><figcaption></figcaption></figure>

* Activate for iOS. On iOS, user consent for ad tracking is managed by the `AppTrackingTransparency` (ATT) system. To activate this parameter, **the success screen** must be deactivated.

<figure><img src="/files/NK9y4eN5ihuTPnTSPqqG" alt=""><figcaption></figcaption></figure>

### Illustration

*\[ Standard, Essential & Premium Plans]*&#x20;

Choose to display illustrations or not on your banner. (only for Clear banner)

### Success Screen

Activate this option if you want the following screen when the user goes beyond the introduction screen and makes mixed choices.&#x20;

<div align="left"><figure><img src="/files/OM6ZAl1eH6ALOko3GY1I" alt="" width="375"><figcaption></figcaption></figure></div>

### Display purposes list on layer 1

<figure><img src="/files/LFuMhul8ejySWqOmMB4Y" alt=""><figcaption></figcaption></figure>

*\[ Standard, Essential & Premium Plans ]*&#x20;

Choose to display or hide the list of processing purposes used by you and your partners on the main page of the banner.

{% hint style="danger" %}
**IMPORTANT**

Please note that the IAB TCF (Transparency and Consent Framework) and the various European DPAs require the purposes of processing to be displayed. If you deactivate this option, it is your responsibility to inform your users in an appropriate manner about the purposes for which their personal data is processed.
{% endhint %}

#### Purposes list display mode

When the purposes list is displayed, you can choose how it appears on the main page of the banner.

* Display purposes list as a dedicated list (default): the processing purposes are shown as a separate, structured list on the banner's main page.
* Display purposes list within the banner text: the processing purposes are listed inline, directly inside the banner's introductory paragraph.

## **UI customization**

*\[ Standard, Essential & Premium Plans ]*&#x20;

To have a notice which fits with your app UI, you can customize some visual assets and colors.

### Banner pictures

* **Add a logo or icon** : Your image will be displayed on your banner.
* **Add a logo or icon to be displayed on the success page** : If you have chosen to display a success screen, choose the image to be displayed on it.

<figure><img src="/files/puvGMcDWGC52vK62Fv2f" alt=""><figcaption></figcaption></figure>

### Buttons and texts color

Depending on whether you choose to integrate the Classic or the Clear consent notice template on your application, please refer to the corresponding section to modify the default UI customization.

Some colors of the notice can be customized with your own colors.

<div><figure><img src="/files/txQuoNTiKUeL1JvPKXWM" alt="" width="563"><figcaption><p>Text color (layer 1)</p></figcaption></figure> <figure><img src="/files/CtJs6VfhDyRoaywjmk3G" alt="" width="563"><figcaption><p>Text color (layer 2)</p></figcaption></figure> <figure><img src="/files/4iubp5JdFFahNY2hGMsC" alt="" width="540"><figcaption><p>Banner background color</p></figcaption></figure></div>

<div><figure><img src="/files/t5SXrOGKwdazCsvbDA66" alt="" width="563"><figcaption><p>Action bar background color</p></figcaption></figure> <figure><img src="/files/trc7O39o8fTlXx2Jr9Un" alt="" width="563"><figcaption><p>Action bar text color</p></figcaption></figure> <figure><img src="/files/5zaRTygzG5aYGUNij5xL" alt="" width="563"><figcaption><p>Status bar background color</p></figcaption></figure></div>

<div><figure><img src="/files/aS5HtsucTxrwsUotmiDu" alt="" width="563"><figcaption><p>Button border color</p></figcaption></figure> <figure><img src="/files/KN3Y8FLLa1c5AB9stFIm" alt="" width="563"><figcaption><p>Button background color</p></figcaption></figure> <figure><img src="/files/4N8CZpJbbPFpprDwl0Pa" alt="" width="563"><figcaption><p>Button text color</p></figcaption></figure></div>

<div><figure><img src="/files/gB3bgMbRIcl48XXFQyte" alt="" width="563"><figcaption><p>Separator color</p></figcaption></figure> <figure><img src="/files/hBhfIcrJq3VI5h4m5ih4" alt="" width="563"><figcaption><p>Button text color (layer 2)</p></figcaption></figure> <figure><img src="/files/0ekMz5ADoQWiRp0Wp9Cj" alt="" width="563"><figcaption><p>Copyright color</p></figcaption></figure></div>

<div><figure><img src="/files/hv8trReJ1Ki0v1CFPhmA" alt="" width="563"><figcaption><p>Switch unset background color</p></figcaption></figure> <figure><img src="/files/NshI5fVOHLBAdZVlZXXg" alt="" width="563"><figcaption><p>Switch on background color</p></figcaption></figure> <figure><img src="/files/X8D0ZaylnjD7zCzjnuXg" alt="" width="563"><figcaption><p>Switch off background color</p></figcaption></figure></div>

<div><figure><img src="/files/qKB4rCs48Pjtdk1pceZs" alt="" width="563"><figcaption><p>Switch on button color</p></figcaption></figure> <figure><img src="/files/KYMTIZWaTjivGD2gkm84" alt="" width="563"><figcaption><p>Switch off button color</p></figcaption></figure> <figure><img src="/files/gGlLSKzpHh8nvlL8Ise4" alt="" width="540"><figcaption><p>Geonotice banner background color</p></figcaption></figure></div>

{% hint style="info" %}
**NOTE**

The fields below are for Box TV notices only:

* Vendor background color dark
* Vendor separator color
* Button selected color
  {% endhint %}

## **Advanced settings**

<figure><img src="/files/iwKLuJXf707MdIeXdChT" alt=""><figcaption></figcaption></figure>

### Floating extra purpose

A floating purpose is a purpose for which only the storage of the consent is concerned. See the **Floating purpose feature** section.

### Enable Legitimate Interest purposes when Refuse All/Continue without accepting (skip)

If the user refuses or leaves, you can decide to collect any data (switch off) or collect only data for purposes under a legitimate interest legal base (switch on)

### Consent retention period

These default values, which we recommend, allow you to be in compliance while maximizing your consent rates. Those durations represent the time lapse before the consent notice will be displayed again and consent will be sought once more.

<figure><img src="/files/6tJIWwOT7RnxyjD6g2TG" alt=""><figcaption></figcaption></figure>

* Accept all: **12 months**
* Refuse all: **6 months**
* Mixed choice: **12 months**
* (if activated) Continue without accepting: **3 days**

You can modify the durations at any time.

{% hint style="info" %}
If you have created your consent notice before October 28th, 2021, the default settings for mobile consent notices are on 12 months for all choices. Feel free now to adjust them.&#x20;
{% endhint %}

## Save the notice

{% hint style="success" %}
Click on **Save**. The notice in Production is updated and ready to be implemented in your application.
{% endhint %}


# 2.3 Actions on a notice

***

## **Edit a notice**

Go to **Notices**, and in the menu on the right, click on the icon corresponding to the modification.

<figure><img src="/files/aRQ9DDQHJzNmjbxfoAHY" alt=""><figcaption></figcaption></figure>

You will end just here :

* **Choose what behavior your banner should have in case of modification**: Note that by default, for any modifications of a consent notice or of its source, the modified notice will only be displayed to new visitors and the notice won't be displayed to previous visitors. If you prefer this last option, you can change the Notice display mode.

<figure><img src="/files/jNPtzfaED5pqvmFo9WY1" alt=""><figcaption></figcaption></figure>

* *\[ Mobile notice only ]* In the **Rollback** section, you can choose to go back to a previous notice version.

<figure><img src="/files/QkpZhyMa9k7nal6XY13Y" alt=""><figcaption></figcaption></figure>

* **Archive**: If you don't need your notice anymore you can archive it. By doing so it will not be on your Notice list anymore.
* **Save**: Saving all your modifications.

## **Duplicate a notice**

You can duplicate a notice from **Notices**, in the list of your notices, by clicking on the duplicate icon.

<figure><img src="/files/jo0hF87ebV6UTZl5Iffq" alt=""><figcaption></figcaption></figure>

## **Archive a notice**

You can also archive a notice from **Notices**, in the list of your notices, by clicking on the archive icon.

<figure><img src="/files/khrMWtzXtHuoU77ICS6L" alt=""><figcaption></figcaption></figure>

## Rebuild a notice

You can update the notice without having to return to the notice modification form after modifying a source.

<figure><img src="/files/f3YHGasDzVdF80W1alVC" alt=""><figcaption></figcaption></figure>

## Preview a notice

In the notices list, click on the **Preview** icon or on **Preview the banner** to preview the notice you want.

<figure><img src="/files/xGsz2CZe2X3PXW3ev1sv" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/OitHiaTeF2TOmUM05Cy6" alt=""><figcaption></figcaption></figure>


# Step 3: Notice implementation (Web / App / TV)

{% content-ref url="/pages/047OPaIL71XhQf8w6FEG" %}
[Web CMP](/configuration/step-3-notice-implementation-web-app-tv/web-cmp)
{% endcontent-ref %}

{% content-ref url="/pages/KEd54l6Mk20C6Mk2Z6Rw" %}
[Google GTM](/configuration/step-3-notice-implementation-web-app-tv/google-gtm)
{% endcontent-ref %}

{% content-ref url="/pages/kEB7OcCS86uUGpJG46OR" %}
[Android](/configuration/step-3-notice-implementation-web-app-tv/android)
{% endcontent-ref %}

{% content-ref url="/pages/WpmUNccEJcvAjmZ2VR9O" %}
[iOS](/configuration/step-3-notice-implementation-web-app-tv/ios)
{% endcontent-ref %}

{% content-ref url="/pages/9cNR4Jix9TvZQKKd0RcM" %}
[React Native](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk/react-native)
{% endcontent-ref %}

{% content-ref url="/pages/1HLHH8vE5tGq5Z1WD0pQ" %}
[Flutter](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk/flutter)
{% endcontent-ref %}

{% content-ref url="/pages/3byOfgQ6uPxWuLyZaFvv" %}
[Unity](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk/unity)
{% endcontent-ref %}


# Web CMP

Implement a notice on a website using Javascript, as described below.

{% hint style="info" %}
**NOTE**

You can find details on each release here : [Release notes page](/help/release-notes).
{% endhint %}

## Main steps to implement a web notice

{% hint style="info" %}
You wish integrate AppConsent into Shopify? See the dedicated page: [Implement Appconsent with Shopify](/configuration/step-3-notice-implementation-web-app-tv/web-cmp/implement-with-shopify)
{% endhint %}

### 1. Implement the notice

{% hint style="info" %}
Open your HTML code source first.
{% endhint %}

1. Go to **Notices** tab in AppConsent configuration interface
2. Then under the notice you wish to integrate, copy the integration code using the "Copy" button

<figure><img src="/files/m0fMM99zWCQfCdtr1iKv" alt=""><figcaption></figcaption></figure>

3. Finally, paste the code in the `<head>` tag in your website

### 2. Add privacy center

With the GDPR, you have an obligation to provide a way for the user to be able to change their choices at any time and easily. That's why you need to add privacy center kit.

To configure and implement the privacy widget: see the [Display privacy widget](/go-further/compliance/display-privacy-widget) page.

### 3. Results

Congratulations! You have now finished setting up the CMP, your code should look like this :

```html
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Your Website</title>
    <!-- MANDATORY: BEGIN IAB STUB -->
    <script type="text/javascript">
        !function(){var e=function(){var e,t="__tcfapiLocator",a=[],n=window;for(;n;){try{if(n.frames[t]){e=n;break}}catch(e){}if(n===window.top)break;n=n.parent}e||(!function e(){var a=n.document,r=!!n.frames[t];if(!r)if(a.body){var s=a.createElement("iframe");s.style.cssText="display:none",s.name=t,a.body.appendChild(s)}else setTimeout(e,5);return!r}(),n.__tcfapi=function(){for(var e,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!n.length)return a;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 s={gdprApplies:e,cmpLoaded:!1,cmpStatus:"stub"};"function"==typeof n[2]&&n[2](s)}else a.push(n)},n.addEventListener("message",(function(e){var t="string"==typeof e.data,a={};try{a=t?JSON.parse(e.data):e.data}catch(e){}var n=a.__tcfapiCall;n&&window.__tcfapi(n.command,n.version,(function(a,r){var s={__tcfapiReturn:{returnValue:a,success:r,callId:n.callId}};t&&(s=JSON.stringify(s)),e&&e.source&&e.source.postMessage&&e.source.postMessage(s,"*")}),n.parameter)}),!1))};"undefined"!=typeof module?module.exports=e:e()}();
    </script>
    <!-- MANDATORY: END IAB STUB -->

    <script type="text/javascript">
        const configSFBXAppConsent = {
            appKey: 'YOUR_APP_KEY'
        }
    </script>
    <script src="https://cdn.appconsent.io/tcf2-clear/current/core.bundle.js" defer async></script>
</head>
<body></body>
</html>
```

***

## Migrated your old configuration

Since version 29.0.0, the implementation of cmp in your website has been simplified. For users who have configured the cmp before this release, we have provided a guide to migrate to the new configuration.

[Configuration Migration Guide](#migrated-your-old-configuration)

***

## Go further

Now that you have implemented your CMP you can put some extra commands or settings by reading the following instructions.

### How heavy is this CMP ?

We're leveraging chunking to alleviate bandwidth. Code for the UI is only downloaded when user interaction is needed. Our core bundle is about 55 KB.

### Advanced properties for the configSFBXAppconsent configuration

All of the properties listed below are **optional.**

#### Debug mode

Active verbose logging in the browser console.

| Property  name | Type    | Default value |
| -------------- | ------- | ------------- |
| debug          | Boolean | false         |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    debug: true,
}
```

#### Activate GCM mode

Enables Google Ads & Analytics blocking.&#x20;

{% hint style="info" %}
We recommend that you enable this feature using the configuration interface ( Back-Office ) instead of using the code.. To learn how to implement GCM with AppConsent, see the [AppConsent configuration for Google Analytics](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/manage-google-analytics#appconsent-configuration-for-google-analytics-google-consent-mode-advanced-mode-gcmv2) section.
{% endhint %}

| Property  name | Type    | Default value |
| -------------- | ------- | ------------- |
| enableGCM      | boolean | false         |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    enableGCM: true,
}
```

#### Enforce compliance with the GDPR

Forces GDPR application for all visitors from any country in the world

| Property  name   | Type    | Default value |
| ---------------- | ------- | ------------- |
| forceGDPRApplies | boolean | false         |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    forceGDPRApplies: true,
}
```

#### Set GDPR applicability&#x20;

Manually sets GDPR applicability: `true` = applies, `false` = doesn’t apply, `null` = automatic

| Property  name | Type            | Default value |
| -------------- | --------------- | ------------- |
| gdprApplies    | boolean \| null | null          |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    gdprApplies: true,
}
```

#### Inject script after user consent

List of script URLs that should loaded and executed after the user consent. If your script relies on consent, use the [addEventListener method of \_\_tcfApi](#addeventlistener)&#x20;

| Property  name       | Type      | Default value |
| -------------------- | --------- | ------------- |
| thirdPartyScriptURLs | string\[] | \[]           |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    thirdPartyScriptURLs: ["https://www.your-site.com/script.js"]
}
```

#### Force uuid consent

For specific use cases like cross-domain or cross-device consent propagation, you can set your proper UUID. You must use unique UUID like UUIDv4 in order to avoid collisionning issue.

| Property  name | Type           | Default value |
| -------------- | -------------- | ------------- |
| uuid           | string \| null | null          |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    uuid: "7ab109-1203-4975-a2e5-c2ac148c3148"
}
```

#### Define time between two configuration checking&#x20;

Defines maximum cache duration between the calls `hello` .

| Property  name          | Type   | Default value |
| ----------------------- | ------ | ------------- |
| cmpVersionCacheDuration | number | 1800          |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    cmpVersionCacheDuration: 1800
}
```

#### Configuration url redirection after user action

Allow to define a redirection url after the user click on "accept all", "refuse all" and "continue without accepting"

| Property  name | Type   | Default value |
| -------------- | ------ | ------------- |
| urlRedirect    | object | null          |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    urlRedirect: {
      main: {
           denyAll : 'someURL',
           acceptAll : 'someURL',
           continueWithoutAccepting : 'someURL',
      },
      settings : {
           denyAll : 'someURL',
           acceptAll : 'someURL',
       }
   }
}
```

#### CMP callbacks

Allows you to define javascript callbacks on the events of cmp. The events are as follows:

* init : Triggers at the end of CMP initialization
* show: Triggers when the CMP is displayed
* listener: Triggered each time the iab listener is triggered
* choiceDone: Triggers when a user gives consent
* adcDetected: Triggers when a user blocks the display of the cmp using the Apple Distraction Control (ADC) feature
* adcUnblocked: Triggers when a user unlocks the CMP display and provides consent

| Property  name | Type   | Default value |
| -------------- | ------ | ------------- |
| enableGCM      | object | null          |

Implementation example:&#x20;

```javascript
var configSFBXAppConsent = {
    appKey: 'YOUR_APPKEY',
    callbacks: {
    init: (error, state) => {},
    show: (error) => {},
    listener: (tcData, success) => {},
    choiceDone: (tcData, success) => {},
    adcDetected: () => {},
    adcUnblocked: () => {},
  }
}
```

### Commands / CMP Calls

CMP can be controlled through iAB's `__tcfApi` global function, as [documented](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#what-required-api-commands-must-a-cmp-support).

#### addEventListener

This IAB command allows you to listen for events related to the consent string.

```javascript
__tcfapi('addEventListener', 2, function (tcData, success) {
    // some codes
})
```

**Getting Consent Object in JS**

Very simple example :

In this example, the `success` variable triggers the code to run once a user has given their consent.

```javascript
__tcfapi("addEventListener", 2, (tcData, success) => {
  if (success) {
    console.log(tcData.tcString);
  } else {
    // do something else
  }
});
```

Will output your tcString V2.3 in your console :

```javascript
CQhXYwAQhXYwAACAKAFRCKFgAPLAAELAAAqIF5wAQF5gXnABAXmAAAAA.IF5wAQF5gAAA.YAAAAAAAAAAA
```

**Working with tcData**

An example of retrieving the status of a purpose :

```javascript
__tcfapi("addEventListener", 2, (tcData, success) => {
  if (success) {
    console.log(tcData.purpose.consents[4]); // Here checking the state of purpose 4
  }
});
```

An example of retrieving the status of a vendor :

```javascript
__tcfapi("addEventListener", 2, (tcData, success) => {
  if (success) {
    console.log(tcData.vendor.consents[755]); // Here checking the state of Google vendor
  }
});
```

Another example of retrieving the status of an extra purpose:&#x20;

```javascript
__tcfapi("addEventListener", 2, (tcData, success) => {
  if (success) {
    console.log(tcData.extraPurpose.consents['taxK3L1v']); // Here checking the state of an extra purpose
  }
});
```

More information in the official IAB Documentation [here](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#addeventlistener).

#### accept

Registers a full consent on the CMP, as the user would have clicked on the "accept everything" button. The default behavior is to prevent overwriting any existing consent. You can force overwriting by specifying a special `force` parameter.

{% hint style="warning" %}
As this command forces an “accept all” action, make sure it is used appropriately
{% endhint %}

Note that no matter the outcome, this call will hide the UI.

| Argument  | Type     | Optional | Value                    |
| --------- | -------- | -------- | ------------------------ |
| command   | string   |          | `'accept'`               |
| version   | number   |          | `2`                      |
| callback  | function |          | `function(error: Error)` |
| parameter | Object   |          | AcceptOption             |

**Example:**

```javascript
__tcfapi("accept", 2, console.log);
```

#### deny

Registers a full consent on the CMP, as the user would have clicked on the "deny everything" button. The default behavior is to prevent overwriting any existing consent. You can force overwriting by specifying a special `force` parameter.

{% hint style="warning" %}
As this command forces an “deny all” action, make sure it is used appropriately
{% endhint %}

Note that no matter the outcome, this call will hide the UI.

| Argument  | Type     | Optional | Value                    |
| --------- | -------- | -------- | ------------------------ |
| command   | string   |          | `'deny'`                 |
| version   | number   |          | `2`                      |
| callback  | function |          | `function(error: Error)` |
| parameter |          |          |                          |

**Example:**

```javascript
__tcfapi("deny", 2, console.log);
```

#### fakeDeny

Generate a deny consent string and return it to all vendors without storing it as a valid user consent. This helps prevent non-compliant vendors from interpreting the absence of consent as granted. This will NOT hide the UI.

| Argument  | Type     | Optional | Value                    |
| --------- | -------- | -------- | ------------------------ |
| command   | string   |          | `'fakedeny'`             |
| version   | number   |          | `2`                      |
| callback  | function |          | `function(error: Error)` |
| parameter |          |          |                          |

**Example:**

```javascript
__tcfapi("fakedeny", 2, console.log);
```

#### setExternalIds

Allows to define additional Ids that will be taken into account when validating user consent.

**Example:**

```javascript
__tcfapi('setExternalIds', 2, () => {}, {strawberry: 'jam', honey: 'mustard'})
```

#### saveExternalIds

This method allows you to save the externalIDs previously added using the `setExternalIds` method to our servers without waiting for user consent.

```javascript
__tcfapi('saveExternalIds', 2, () => {})
```

#### getExternalIds

Retrieves the stored external IDs

```javascript
__tcfapi('getExternalIds', 2, () => {})
```

#### getUuid

This method return the current UUID, or null if not available yet

```javascript
__tcfapi('getUuid', 2, (uuid) => { console.log(uuid) })
```

| Argument  | Type     | Optional | Value                          |
| --------- | -------- | -------- | ------------------------------ |
| command   | string   |          | `'`getUuid`'`                  |
| version   | number   |          | `2`                            |
| callback  | function |          | `function(uuid: String\|null)` |
| parameter |          |          |                                |

### Passing commands in URL

You can pass commands to the CMP through the querystring. Querystring commands are evaluated on init.

`?ac_cmd=show`

The above link would show the CMP on init.

For more specific needs, other orders exist. Please contact the [support](mailto:support-cmp@sfbx.io).

You can also pass parameters to the command with the same mechanism. Parameters are passed as is from the querystring. Consider the following example:

`?ac_cmd=show&jumpAt=banner`

### Edition of consents without displaying the CMP

When you website embeds external integrations (Youtube, Twitch, Twitter, etc.), you may need to modify consents of a user (with their agreement) to give access to this content without redisplaying the CMP. To do this, you can use the `updateStatus` function. This is how to use it:

```javascript
__tcfapi('updateStatus', 2, () => {}, [{t: <type>, id: <id>, status: <bool-value> }])
```

The list of possible types `<type>` is the following one:

1 - Purpose

2 - Extra purpose

3 - Special feature

4 - IAB vendor

5 - Extra vendor

The ID `<id>` is the one of the object you want to enable/disable. Its nature depends on the object represented by `<type>`. You can see the list of available types and IDs for your notice through `getTCData` call (see usage example here).

The value `<bool-value>` enables (`true`) or disables (`false`) for the tuple `(<type>, <id>)`.

An error will be returned if the consent of the user does not exist yet, of if any of the input combinations `(<type>, <id>)` does not exist in the array in argument.

To find the latest updates of the CMP, see the [Release Notes](/help/release-notes) section.


# Implement with Shopify

On this page you'll find instructions on how to implement AppConsent CMP with Shopify.

***

### 1. Open the code edition of your Shopify template

In Shopify :

1. Go to Online Store → Themes
2. Click on the "..." button
3. Click on "Edit code"

<figure><img src="/files/eJde6ym0INjeuNqmHFqA" alt=""><figcaption></figcaption></figure>

### 2. Add the web CMP in the theme code

1. In the side menu, open the theme file containing your site's html base. For our example theme, we'll use theme.liquid
2. Then place yourself before the end of the `<head>` tag.

<figure><img src="/files/DQzBGO1XFJT4qHB4JCG0" alt=""><figcaption></figcaption></figure>

3. And add the following code

```

<div data-gb-custom-block data-tag="unless" data-0='Shopify.designMode' data-1='Shopify.designMode'>

// PUT THE CODE OF CMP IMPLEMENTATION HERE

</div>

```

{% hint style="info" %}
**INFO**

We have to wrap the cmp implementation code, as shopify's customization mode is not compatible with the CMP code.
{% endhint %}

4. Retrieve the implementation code for your notice, by going to the **Notices** tab in the configuration interface, then under the notice you wish to integrate, copy the integration code using the "Copy" button.
5. Delete the text "PUT THE CODE OF CMP IMPLEMENTATION HERE" from the shopify implementation and paste the code from the web implementation instead
6. Save your theme page, and you should have an implementation that looks like this:

```

<div data-gb-custom-block data-tag="unless" data-0='Shopify.designMode' data-1='Shopify.designMode'>

<!-- MANDATORY: BEGIN IAB STUB -->
<script type="text/javascript">
  !function(){var e=function(){var e,t="__tcfapiLocator",a=[],n=window;for(;n;){try{if(n.frames[t]){e=n;break}}catch(e){}if(n===window.top)break;n=n.parent}e||(!function e(){var a=n.document,r=!!n.frames[t];if(!r)if(a.body){var s=a.createElement("iframe");s.style.cssText="display:none",s.name=t,a.body.appendChild(s)}else setTimeout(e,5);return!r}(),n.__tcfapi=function(){for(var e,t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!n.length)return a;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 s={gdprApplies:e,cmpLoaded:!1,cmpStatus:"stub"};"function"==typeof n[2]&&n[2](s)}else a.push(n)},n.addEventListener("message",(function(e){var t="string"==typeof e.data,a={};try{a=t?JSON.parse(e.data):e.data}catch(e){}var n=a.__tcfapiCall;n&&window.__tcfapi(n.command,n.version,(function(a,r){var s={__tcfapiReturn:{returnValue:a,success:r,callId:n.callId}};t&&(s=JSON.stringify(s)),e&&e.source&&e.source.postMessage&&e.source.postMessage(s,"*")}),n.parameter)}),!1))};"undefined"!=typeof module?module.exports=e:e()}();
</script>
<!-- MANDATORY: END IAB STUB -->

<script type="text/javascript">
  const configSFBXAppConsent = {
    appKey: 'YOUR APPKEY'
  }
</script>
<script src="https://cdn.appconsent.io/tcf2-clear/current/core.bundle.js" defer async></script>

</div>
```


# Privacy Center Kit

Find out why it is necessary to add a Privacy Widget to your site and how to implement it.

***

Discover [how to implement the privacy widget](/go-further/compliance/display-privacy-widget).


# Prebid.js

Prebid.js is a feature-rich header bidding platform for the web, including more than 150 demand sources and 15 analytics adapters. It supports currency conversion, GDPR, common ID systems, and multiple ad servers.

***

## Use prebid.js with Appconsent CMP

1. Add the AppConsent CMP code to your website
2. Download the prebuild version on <https://docs.prebid.org/download.html> or build it yourself from <https://github.com/prebid/Prebid.js>
3. Place your prebid.js file in your web directory
4. In your website, add the following configuration script for prebid.js, with the *consentManagement* configuration (Take care of loading this script before loading the cmp snippet):

```html
<script>
  var PREBID_TIMEOUT = 300;
  var pbjs = pbjs || {};
  pbjs.que = pbjs.que || [];
  pbjs.que.push(function()
                {
    pbjs.setConfig({consentManagement:
                     { cmpApi: 'iab', //needs to be iab 
                       timeout: 8000, //timeout for prebid to wait for consent in ms 
                       allowAuctionWithoutConsent: false //send requests without consent? 
                     }
                    });
    var units = [];
    units[units.length] = { 
      code: "content", 
      sizes: [[300, 250]], 
      bids: [ 
        {bidder: "criteo", params: {zoneId: "..."}}, 
        {bidder: "fidelity", params: {zoneid: "...", floor: 0.05, server: "..."}}, 
        {bidder: "stroeerCore", params: {sid: "..."}} 
        //more bidders here 
      ]};
    pbjs.addAdUnits(units);
    pbjs.timeout = 300;
    pbjs.requestBids({ bidsBackHandler: function(bidResponses){ }, timeout: 300 });
  });
</script>
```

{% hint style="warning" %}
**ALLOW AUCTION WITH OR WITHOUT CONSENTS**

In this code, **`allowAuctionWithoutConsent`** is **`false`**. Meaning that requests will not be sent without consent. If you want to send requests without consent, set it to **`true`**
{% endhint %}

{% hint style="info" %}
**INFO**

If you are using Tag commander or GTM, please refer to your Prebid representatives to ask them the best way to implement the script.
{% endhint %}


# AMP

***

## IAB Compliance

We are a valid registered CMP at the TCF ( ID\_CMP = 2 ). We currently support the last stable version of the TCFV2. We also issued an AMP version that is now available. You can have a check [here](https://amp.dev/documentation/components/amp-consent/?format=websites).

As IAB Member, we are also member and contributor of the french IAB Privacy Task Force.

The AMP developer website gives general informations on how to implement an AMP CMP. Make sure you are familiar with it before proceeding: <https://amp.dev/documentation/components/amp-consent/>

***

## Embed amp-consent tag

Add the amp-consent script to your AMP page:

```javascript
<script async custom-element="amp-consent" src="https://cdn.ampproject.org/v0/amp-consent-0.1.js"></script>
```

***

## Generate a Notice in AppConsent

This does not change from other implementations. Create a CMP on a Web Source.

#### Configure the amp-consent tag with AppConsent specifics

This tag will have to be added to the body of your HTML.

```markup
<amp-consent id='foobar' layout='nodisplay' type='appconsent'>
  <script type="application/json">
  {
    "clientConfig": {
      "appKey": "simple",
      "debug": true
    }
  }
  </script>
</amp-consent>
```

`type='appconsent'` in `<amp-consent>` is really important as it tells AMP which CMP to use.

The `appKey` is the same as your web notice .Get your appKey directly from the Backoffice by clicking on a notice and copy/past.

The object `clientConfig` contains the AppConsent configuration required to make the AMP SDK works.

#### Add the `amp-consent-blocking meta` tag at the top of your page

You have to add `<meta name="amp-consent-blocking" content="">` at the top of your page to avoid having an error in the AMP validator.

This meta tag allows you to block a set of tags for the entire page. For example `<meta name="amp-consent-blocking" content="amp-ad">` would block all the amp-ad tags on the page.

If you decide not to use that feature to block a particular tag, you still have to add that tag with an empty content.

#### Add a link for the user to manage their preferences

After the user has given consent or closed the consent notice, you must give them an easy access to their choices so that they can update them.

To create that link that you have to add a postPromptUI element to your AppConsent SDK tag:

```markup
<amp-consent layout="nodisplay" id="cmp" type="appconsent">
    <script type="application/json">
    {
        "postPromptUI": "postPromptUIInstance",
        "clientConfig": {...}
    }
    </script>

    <div id="postPromptUIInstance">
        You can manage your consents by clicking here :
        <button on="tap:cmp.prompt()" role="button">Manage</button>
    </div>
</amp-consent>
```

`postPromptUI` is optional and allow you to have a banner at the bottom of the page if the user wants to reopen the banner to manage his consents, in case a consent has already been given.

`id='cmp'` in `<amp-consent>` is used to reference the node, for instance for `on="tap:cmp.prompt()"` to work properly if you use `postPromptUI`.


# Migration of the CMP implementation

Since version 32.0.0 of the CMP, the implementation in your website has been simplified. In the page, we will see how to migrate step by step from old implementation to the new one.

***

## New implementation

The code to be implemented in your website is provided under each notice in the configuration interface and can be easily copied using the "Copy" button. All that remains is to paste the code into the `<head>` section of your website.

## Event listener

The event listener has been integrated directly into the notice, so it is no longer necessary to add it on all the pages of your website. You can therefore **remove** the following script from your website.&#x20;

{% hint style="danger" %}
**TO DELETE**

```html
<!-- ADD EVENTLISTENER -->
<script type="text/javascript">
    (adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=1,window.dataLayer=window.dataLayer||[],__tcfapi("addEventListener",2,function(e,n){if(n&&e.gdprApplies&&("tcloaded"===e.eventStatus||"useractioncomplete"===e.eventStatus)){if((adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=0,e.purpose.consents)
        for(var s in window.dataLayer.push({AppConsent_IAB_PURPOSES:e.purpose.consents}),e.purpose.consents)e.purpose.consents[s]&&window.dataLayer.push({event:"appconsent_ctrl_"+s});var o,a;e.acExtraPurposes&&(o={},e.acExtraPurposes.forEach(function(e){o[e]=!0}),window.dataLayer.push({AppConsent_EXTRA_PURPOSES:o})),e.acExtraVendors&&(a={},e.acExtraVendors.forEach(function(e){a[e]=!0}),window.dataLayer.push({AppConsent_EXTRA_VENDORS:a})),e.purpose.consents&&e.vendor.consents&&("object"==typeof sfbxguardian&&e.purpose.consents[1]&&window.sfbxguardian.unblock(),"function"==typeof gtag&&(e.purpose.consents[1]&&e.vendor.consents[755]?gtag("consent","update",{analytics_storage:e.purpose.consents[7]||e.purpose.consents[9]?"granted":"denied",ad_storage:e.purpose.consents[3]?"granted":"denied "}):gtag("consent","update",{analytics_storage:"denied",ad_storage:"denied"})))}window.dataLayer.push({event:"appconsent_loaded"})});
</script>
<!-- END EVENTLISTENER -->
```

{% endhint %}

## The `init` and `show` commands

From now on, the `init` and `show` commands are automatically executed at the initialization of the notice and this implies changes in the implementation of the notice.

### The `init` command

Previously, it was necessary to implement the following script in the `<body>` section of HTML pages.&#x20;

{% hint style="danger" %}
**TO DELETE**

```html
<script type="text/javascript">
    __tcfapi('init', 2, console.log, {
        appKey: 'YOUR_APP_KEY',
        url: 'https://collector.appconsent.io',
        // targetCountries: ['FR', 'UK', 'US'],
        // forceGDPRApplies: true,
    })
</script>
```

{% endhint %}

#### **Callback of the `init` command**

If you have defined a callback in the `init` command, you must now add it to the `configSFBXAppConsent` configuration variable in the `callbacks` parameter.

As a reminder, the callback will be executed at the end of the initialization of the notice. And takes as input parameters `error` and `state`.

{% hint style="info" %}
**INFO**

In the documentation for the old implementation, we suggested putting `console.log` as a callback to the command. If you are in this case, it is no longer necessary to set it as callback.&#x20;
{% endhint %}

Example of `init` callback configuration :

```html
<script type="text/javascript">
    const configSFBXAppConsent = {
        appKey: 'YOUR_APP_KEY',
        callbacks: {
            init: ( error, state ) => {},
        }
    }
</script>
```

### The `show` command

Same as the `init` command, the `show` command no longer needs to be implemented like this :

{% hint style="danger" %}
**TO DELETE**

```html
<script type="text/javascript">
    __tcfapi('show', 2, console.log, {
        lazy: true,
    })
</script>
```

{% endhint %}

#### **Callback of `show` command**

If you defined a callback in the `show` command, you must now add it to the `configSFBXAppConsent` configuration variable in `callbacks` parameter.

As a reminder, this callback will be executed at the end of the command execution. And takes as input parameter `error`.

{% hint style="info" %}
**INFO**

In the example code of the old implementation, we proposed in the documentation to put `console.log` in the callback of the command If you are in this case, it is no longer necessary to put it in callback.&#x20;
{% endhint %}

Example of callback configuration :

```html
<script type="text/javascript">
    const configSFBXAppConsent = {
        appKey: 'YOUR_APP_KEY',
        callbacks: {
            show: ( error ) => {}
        }
    }
</script>
```

### Change in the configuration of the CMP

#### **Option `url`**

The `url` option has now the following default value : <https://collector.appconsent.io>. It is not necessary to have this option in your cmp configuration, **if** you use this url.

{% hint style="danger" %}
**OLD IMPLEMENTATION**

```html
<script type="text/javascript">
    __tcfapi('init', 2, console.log, {
      appKey: 'YOUR_APP_KEY',
      url: 'https://collector.appconsent.io',
    })
</script>
```

{% endhint %}

{% hint style="info" %}
**NEW IMPLEMENTATION**

```markup
<script type="text/javascript">
    const configSFBXAppConsent = {
        appKey: 'YOUR_APP_KEY'
    }
</script>
```

{% endhint %}

#### **Option `lazy`**

The `lazy` option is now enabled by default, if you had enabled the `lazy` option, it is no longer required to have it in the configuration.&#x20;

{% hint style="danger" %}
**OLD IMPLEMENTATION**

```markup
<script type="text/javascript">
    __tcfapi('show', 2, console.log, {
        lazy: true,
    })
</script>
```

{% endhint %}

{% hint style="info" %}
**NEW IMPLEMENTATION**

```markup
<script type="text/javascript">
    const configSFBXAppConsent = {
        appKey: 'YOUR_APP_KEY'
    }
</script>
```

{% endhint %}

## Guardian

If you haven't implemented guardian, you can skip this chapter.

The guardian configuration has been changed. Previously, it was necessary to add a `<script>` tag in which you defined the urls you wanted to blacklist or whitelist.&#x20;

{% hint style="danger" %}
**OLD IMPLEMENTATION**

```markup
<script>
    window.SFBX_GUARDIAN_BLACKLIST = [
        /facebook/, /youtube/,
    ]
    // Or a whitelist
    window.SFBX_GUARDIAN_WHITELIST = [
        /appconsent/,
    ]
</script>
```

{% endhint %}

Now this configuration must be put in the configuration variable **configSFBXAppConsent** with the parameter **dynamicallyLoadedScripts** with two subparts `blacklist` and `whitelist`.

{% hint style="success" %}

```markup
<script type="text/javascript">
    const configSFBXAppConsent = {
        appKey: 'YOUR_APP_KEY',
        dynamicallyLoadedScripts: {
            blacklist: [
                /facebook/, /youtube/,
            ],
            whitelist: [
                /appconsent/,
            ]
        },
    }
</script>
```

{% endhint %}

The Guardian script has been changed to accommodate this new way of configuring Guardian. So you have to replace the old `<script>` tag containing the Guardian code with this one:

```html
<script>
    !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).sfbxguardian={})}(this,function(t){"use strict";function o(e,t){return e&&(!t||t!==c)&&(!s.blacklist||s.blacklist.some(function(t){return t.test(e)}))&&(!s.whitelist||s.whitelist.every(function(t){return!t.test(e)}))}function l(t){var e=t.getAttribute("src");return s.blacklist&&s.blacklist.every(function(t){return!t.test(e)})||s.whitelist&&s.whitelist.some(function(t){return t.test(e)})}if(typeof configSFBXAppConsent === 'undefined'){console.error('SFBX Guardian has not been executed! The configuration (configSFBXAppConsent) must be placed before calling the script '); return}if(!configSFBXAppConsent.dynamicallyLoadedScripts||(!configSFBXAppConsent.dynamicallyLoadedScripts.blacklist&&!configSFBXAppConsent.dynamicallyLoadedScripts.whitelist)){console.warn('SFBX Guardian: No whitelist or blacklist has been defined'); return;}var c="javascript/blocked",s={blacklist:configSFBXAppConsent.dynamicallyLoadedScripts.blacklist,whitelist:configSFBXAppConsent.dynamicallyLoadedScripts.whitelist},u={blacklisted:[]},f=new MutationObserver(function(t){for(var e=0;e<t.length;e++)for(var i=t[e].addedNodes,r=function(t){var r=i[t];if(1===r.nodeType&&"SCRIPT"===r.tagName){var e=r.src,n=r.type;if(o(e,n)){u.blacklisted.push([r,r.type]),r.type=c;r.addEventListener("beforescriptexecute",function t(e){r.getAttribute("type")===c&&e.preventDefault(),r.removeEventListener("beforescriptexecute",t)}),r.parentElement&&r.parentElement.removeChild(r)}}},n=0;n<i.length;n++)r(n)});f.observe(document.documentElement,{childList:!0,subtree:!0});var i=document.createElement,a={src:Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src"),type:Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"type")};function p(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(t)))return;var r=[],n=!0,i=!1,o=void 0;try{for(var c,a=t[Symbol.iterator]();!(n=(c=a.next()).done)&&(r.push(c.value),!e||r.length!==e);n=!0);}catch(t){i=!0,o=t}finally{try{n||null==a.return||a.return()}finally{if(i)throw o}}return r}(t,e)||r(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function d(t){return function(t){if(Array.isArray(t))return n(t)}(t)||function(t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(t))return Array.from(t)}(t)||r(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function r(t,e){if(t){if("string"==typeof t)return n(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r<e;r++)n[r]=t[r];return n}document.createElement=function(){for(var t=arguments.length,e=new Array(t),r=0;r<t;r++)e[r]=arguments[r];if("script"!==e[0].toLowerCase())return i.bind(document).apply(void 0,e);var n=i.bind(document).apply(void 0,e);try{Object.defineProperties(n,{src:{get:function(){return a.src.get.call(this)},set:function(t){o(t,n.type)&&a.type.set.call(this,c),a.src.set.call(this,t)}},type:{set:function(t){var e=o(n.src,n.type)?c:t;a.type.set.call(this,e)}}}),n.setAttribute=function(t,e){"type"===t||"src"===t?n[t]=e:HTMLScriptElement.prototype.setAttribute.call(n,t,e)}}catch(t){console.warn("sfbxguardian: unable to prevent script execution for script src ",n.src,".\n",'A likely cause would be because you are using a third-party browser extension that monkey patches the "document.createElement" function.')}return n};var y=new RegExp("[|\{}()[\\]^$+*?.]","g");t.unblock=function(){for(var t=arguments.length,r=new Array(t),e=0;e<t;e++)r[e]=arguments[e];r.length<1?(s.blacklist=[],s.whitelist=[]):(s.blacklist&&(s.blacklist=s.blacklist.filter(function(e){return r.every(function(t){return"string"==typeof t?!e.test(t):t instanceof RegExp?e.toString()!==t.toString():void 0})})),s.whitelist&&(s.whitelist=[].concat(d(s.whitelist),d(r.map(function(e){if("string"==typeof e){var r=".*"+e.replace(y,"\\$&")+".*";if(s.whitelist.every(function(t){return t.toString()!==r.toString()}))return new RegExp(r)}else if(e instanceof RegExp&&s.whitelist.every(function(t){return t.toString()!==e.toString()}))return e;return null}).filter(Boolean)))));for(var n=document.querySelectorAll('script[type="'.concat(c,'"]')),i=0;i<n.length;i++){var o=n[i];l(o)&&(u.blacklisted.push([o,"application/javascript"]),o.parentElement.removeChild(o))}var a=0;d(u.blacklisted).forEach(function(t,e){var r=p(t,2),n=r[0],i=r[1];if(l(n)){var o=document.createElement("script");for(var c in"undefined"!==n.src&&o.setAttribute("src",n.src),o.setAttribute("type",i||"application/javascript"),n)c.startsWith("on")&&(o[c]=n[c]);document.head.appendChild(o),u.blacklisted.splice(e-a,1),a++}}),s.blacklist&&s.blacklist.length<1&&f.disconnect()},Object.defineProperty(t,"__esModule",{value:!0})});
</script>
```

## GCM (Google Consent Mode v2)

Please go tho this page to get last instructions to use Google Consent Mode v2 with our CMP.

[How to implement GCM](/configuration/step-3-notice-implementation-web-app-tv/web-cmp/setting-up-google-consent-mode-v2-for-google-analytics-and-google-ads)

### Implementing our CMP using Google Tag Manager

One of the solution proposed was to use your tag manager to trigger Google Analytics tags based on the purpose\_events sent to the dataLayer. Depending on what you have implemented in your pages, you can remove it to migrate to the new implementation.

For more information, see page : [Install AppConsent with Google Tag Manager](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/install-appconsent-with-google-tag-manager)


# Blocking tags using Guardian

Asking consent is not enough for your website, you need to be sure that cookies or others tracking technologies are not used without the consent of the user.

**For this, we propose a script called Guardian.**

{% hint style="info" %}
**INFO**

At this point, we assume that you followed the instructions in this section to install the CMP
{% endhint %}

## 1. Setup the whitelist/blacklist list in the `<head>`

In the `<head>` section, configure the urls to be blacklisted or whitelisted in the **dynamicallyLoadedScripts** in the parameter of the **configSFBXAppConsent** configuration variable like this :

```html
<script type="text/javascript">
    const configSFBXAppConsent = {
        appKey: 'YOUR_APP_KEY',
        dynamicallyLoadedScripts: {
            blacklist: [
                /facebook/, /youtube/,
            ],
            whitelist: [
                /appconsent/,
            ]
        },
    }
</script>
```

{% hint style="info" %}
**INFO**

In the code above, we take control on Facebook and Youtube. If you want to add a new domain, let's say **ads-twitter.com, just add this new entry**

```
,/ads-twitter.com/
```

{% endhint %}

## 2. Add the Guardian script

Add the Guardian script in the `<head>` section, after the definition of the **configSFBXAppConsent** variable.

```html
<script>
    (function(h,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(h=typeof globalThis<"u"?globalThis:h||self,p(h.sfbxguardian={}))})(this,function(h){"use strict";let p=!1;function P(t){function e(i){for(var s=0,r=0;r<i.length;r++)s=i.charCodeAt(r)+((s<<5)-s);return s}function n(i){return"hsl("+e(i)%360+", 100%, 80%)"}return function(i,...s){if(p&&console.log){if(i instanceof Error&&console.error){console.error(i);return}console.log("%c "+t+" %c "+i,"background: "+n(t)+"; color: #000","",...s)}}}function _(t){if(t===void 0)return p;p=!!t}const B=()=>{const t=window.location.search.substr(1).split("&");if(t==="")return{};for(var e={},n=0;n<t.length;++n){var i=t[n].split("=",2);if(i.length===1)e[i[0]]="";else{var s=decodeURIComponent(i[1].replace(/\+/g," "));s==="false"&&(s=!1),s==="0"&&(s=0),s==="true"&&(s=!0),s===""&&(s=null),e[i[0]]=s}}return e},d=P("guardian"),H=()=>{const t=B();(t.ac_cmd&&t.ac_cmd==="debug"||configSFBXAppConsent&&configSFBXAppConsent.debug)&&_(!0)},f="javascript/blocked",F="application/javascript",c={SCRIPT:"SCRIPT",IFRAME:"IFRAME",BLOCKQUOTE:"BLOCKQUOTE"},w=[c.SCRIPT,c.IFRAME,c.BLOCKQUOTE],o={blacklist:configSFBXAppConsent.dynamicallyLoadedScripts.blacklist,whitelist:configSFBXAppConsent.dynamicallyLoadedScripts.whitelist},l={blacklisted:[],hiddenBlacklisted:[]},U="sfbx_guardian_",b={INSTAGRAM:"instagram-media",TWITTER:"twitter-tweet",TIKTOK:"tiktok-embed"},g={};g[b.INSTAGRAM]="instagram.com",g[b.TIKTOK]="tiktok.com",g[b.TWITTER]="twitter.com";const D=[b.INSTAGRAM,b.TWITTER,b.TIKTOK],k=t=>{switch(t.tagName){case c.BLOCKQUOTE:return j(t);case c.IFRAME:case c.SCRIPT:return K(t)}},K=t=>t.src&&(!t.type||t.type!==f)&&E(t.src),j=function(t){return t.className.split(" ").some(n=>D.includes(n)&&W(n))},W=function(t){const e=g[t];return E(e)},E=t=>(!o.blacklist||o.blacklist.some(e=>e.test(t)))&&(!o.whitelist||o.whitelist.every(e=>!e.test(t))),v=t=>{switch(t.tagName){case c.BLOCKQUOTE:return q(t);case c.IFRAME:case c.SCRIPT:return X(t)}},X=function(t){const e=t.getAttribute("src");return A(e)},q=function(t){return t.className.split(" ").some(n=>G(n))},G=function(t){const e=g[t];return A(e)};function A(t){return o.blacklist&&o.blacklist.every(e=>!e.test(t))||o.whitelist&&o.whitelist.some(e=>e.test(t))}const Q=t=>t.offsetHeight>0&&t.offsetWidth>0,u=(t,e)=>{Object.keys(e).forEach(n=>{t.style[n]=e[n]})},$=function(){let t="";const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let n=0;n<8;n++)t+=e.charAt(Math.floor(Math.random()*e.length));return t},S="#0252B6",V={width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center"},z={display:"flex",flexDirection:"column",borderRadius:"9px",backgroundColor:"white",padding:"20px",border:`1px solid${S}`,fontFamily:"Montserrat, Roboto, Tahoma, Helvetica, Arial, sans-serif"},Y={display:"flex",justifyContent:"center",marginTop:"20px"},J={display:"flex",justifyContent:"center",alignItems:"center",padding:"10px 20px",borderRadius:"6px",fontStyle:"normal",fontWeight:"500",fontSize:"1.1rem",borderColor:`1px solid ${S}`,cursor:"pointer",backgroundColor:`${S}`,color:"white",boxShadow:"none",letterSpacing:"0.05rem"},Z={fontSize:"1rem",fontWeight:400,color:"#02244F",lineHeight:"1.6rem"};class ee{constructor(e,n,i){this.node=e,this.parentElement=i,this.id=n,this.isVisible=!0,this.wrapper=document.createElement("div"),this.init()}init(){this.isVisible=Q(this.node),this.isVisible||this.addToHiddenBlacklisted(),this.buildHtml()}addToHiddenBlacklisted(){y.observe(this.parentElement),C.observe(this.parentElement),l.hiddenBlacklisted.push(this)}buildHtml(){u(this.wrapper,V),this.wrapper.setAttribute("id",this.id),this.buildContainer()}buildContainer(){const e=document.createElement("div");e.classList.add("message-container"),u(e,z),this.isVisible||u(e,{display:"none"}),e.appendChild(this.buildHeader()),e.appendChild(this.buildMessage()),e.appendChild(this.buildButton()),this.wrapper.appendChild(e)}buildHeader(){return document.createElement("div")}buildMessage(){const e=document.createElement("aside");return u(e,Z),e.innerHTML=this.buildDefaultText(),e}buildDefaultText(){return"<p>Le contenu est bloqué, car vous avez décidé de refuser les cookies et usages nécessaires à son affichage.</p>  <p>Usages nécessaires : </p> <ul style='margin-bottom: 16px'><li>Interactions avec les réseaux sociaux : Ces cookies vous permettent de partager des contenus de notre site, d’interagir avec les réseaux sociaux et d'afficher des contenus provenant des réseaux sociaux.</li></ul>"}buildButton(){const e=document.createElement("div"),n=document.createElement("div");return u(e,Y),u(n,J),n.innerHTML="Modifier mon consentement",n.onclick=this.callbackDisplayCMP,e.appendChild(n),e}callbackDisplayCMP(){window.__tcfapi("show",2,()=>{},{})}getMessageContainer(){return this.wrapper.querySelector(".message-container")}}class te{constructor(e){this.node=e,this.oldType=null,this.parentElement=this.node.parentElement,this.message=null,this.id=`${U}${$()}`,this.init()}init(){const{tagName:e}=this.node;switch(e){case c.SCRIPT:this.blockScript();break;case c.IFRAME:case c.BLOCKQUOTE:this.blockElementWithSrc();break}this.removeNodeToDocument()}blockScript(){const{type:e}=this.node;this.oldType=e,this.node.setAttribute("type",f),this.addBeforeScriptListener()}blockElementWithSrc(){this.addWrapper()}addBeforeScriptListener(){const e=function(n){this.node.getAttribute("type")===f&&n.preventDefault(),this.node.removeEventListener("beforescriptexecute",e)};this.node.addEventListener("beforescriptexecute",e)}removeNodeToDocument(){this.parentElement&&this.parentElement.removeChild(this.node)}addWrapper(){d("add wrapper",this.id,this.node,this.parentElement),this.message=new ee(this.node,this.id,this.parentElement),this.parentElement.appendChild(this.message.wrapper)}}const O=new MutationObserver(t=>{for(let e=0;e<t.length;e++){const{addedNodes:n}=t[e];for(let i=0;i<n.length;i++){const s=n[i],{tagName:r,nodeType:a}=s;a===1&&w.includes(r)&&d("checking",k(s),s),a===1&&w.includes(r)&&k(s)&&(L(s),d("observer",l))}}}),y=new ResizeObserver(t=>{t.forEach(e=>{const n=I(e.target);if(n){const i=n.getMessageContainer(),s=e.target.offsetHeight>0?"flex":"none";d("resizeObserver",e.target,n.id,s,e.target.offsetHeight),u(i,{display:s})}})}),C=new IntersectionObserver(t=>{t.forEach(e=>{const n=I(e.target);if(n){const i=n.getMessageContainer(),s=e.isIntersecting&&e.target.offsetHeight>0?"flex":"none";d("intersectionObserver",e.target,n.id,s,e.target.offsetHeight),u(i,{display:s})}})});function I(t){return l.hiddenBlacklisted.find(e=>t&&e.parentElement.isEqualNode(t))}function L(t){const e=new te(t);l.blacklisted.push(e)}const ne=()=>{O.observe(document.documentElement,{childList:!0,subtree:!0})},x=document.createElement,m={src:Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"src"),type:Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,"type")};document.createElement=function(...t){if(t[0].toLowerCase()!=="script")return x.bind(document)(...t);const e=x.bind(document)(...t);try{Object.defineProperties(e,{src:{...m.src,set(n){E(n)&&m.type.set.call(this,f),m.src.set.call(this,n)}},type:{...m.type,get(){const n=m.type.get.call(this);return n===f||E(this.src)?null:n},set(n){const i=k(e.src)?f:n;m.type.set.call(this,i)}}}),e.setAttribute=function(n,i){n==="type"||n==="src"?e[n]=i:HTMLScriptElement.prototype.setAttribute.call(e,n,i)}}catch{console.warn("sfbxguardian: unable to prevent script execution for script src ",e.src,`.
`,'A likely cause would be because you are using a third-party browser extension that monkey patches the "document.createElement" function.')}return e};const se=new RegExp("[|\\{}()[\\]^$+*?.]","g"),ie=function(...t){oe(t),re();let e=0;[...l.blacklisted].forEach((n,i)=>{const{id:s,node:r,oldType:a}=n;if(v(r)){switch(r.tagName){case c.SCRIPT:ce(r,a);break;case c.IFRAME:N(s),le(r,s);break;case c.BLOCKQUOTE:N(s),ae(r,s);break}d("node unblocked",r),l.blacklisted.splice(i-e,1),e++}}),o.blacklist&&o.blacklist.length<1&&(O.disconnect(),y.disconnect(),C.disconnect(),d("observers disconnected"))},N=t=>{const e=l.hiddenBlacklisted.find(n=>n.id===t);e&&(y.unobserve(e.parent),C.unobserve(e.parent),l.hiddenBlacklisted=l.hiddenBlacklisted.filter(n=>n.id!==t))};function oe(t){t.length<1?(o.blacklist=[],o.whitelist=[]):(o.blacklist&&(o.blacklist=o.blacklist.filter(e=>t.every(n=>{if(typeof n=="string")return!e.test(n);if(n instanceof RegExp)return e.toString()!==n.toString()}))),o.whitelist&&(o.whitelist=[...o.whitelist,...t.map(e=>{if(typeof e=="string"){const i=".*"+e.replace(se,"\\$&")+".*";if(o.whitelist.every(s=>s.toString()!==i.toString()))return new RegExp(i)}else if(e instanceof RegExp&&o.whitelist.every(n=>n.toString()!==e.toString()))return e;return null}).filter(Boolean)]))}function re(){const t=document.querySelectorAll(`script[type="${f}"]`);for(let e=0;e<t.length;e++){const n=t[e];v(n)&&L(n)}}function ce(t,e){const n=document.createElement("script");for(let i=0;i<t.attributes.length;i++){let s=t.attributes[i];s.name!=="src"&&s.name!=="type"&&n.setAttribute(s.name,t.attributes[i].value)}n.setAttribute("src",t.src),n.setAttribute("type",e||F),document.head.appendChild(n)}function le(t,e){M(t,"iframe",e).setAttribute("src",t.src)}function ae(t,e){const n=M(t,"blockquote",e),i=t.children;for(let s=0;s<i.length;s++)n.appendChild(i[s].cloneNode(!0))}function M(t,e,n){const i=document.getElementById(n),s=i.parentElement,r=document.createElement(e);for(let a=0;a<t.attributes.length;a++){let T=t.attributes[a];T.name!=="src"&&T.name!=="type"&&r.setAttribute(T.name,t.attributes[a].value)}return s.removeChild(i),s.appendChild(r),r}if(typeof configSFBXAppConsent>"u")throw console.error("SFBX Guardian has not been executed! The configuration (configSFBXAppConsent) must be placed before calling the script "),Error();if(!configSFBXAppConsent.dynamicallyLoadedScripts||!configSFBXAppConsent.dynamicallyLoadedScripts.blacklist&&!configSFBXAppConsent.dynamicallyLoadedScripts.whitelist)throw console.warn("SFBX Guardian: No whitelist or blacklist has been defined"),Error();function de(){H(),d("init"),ne()}const R=B();(!R.ac_cmd||R.ac_cmd!=="no_guardian")&&de(),h.unblock=ie,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})});
</script>
```

{% hint style="danger" %}
**IMPORTANT**

&#x20;The Guardian script must be placed after the configSFBXAppConsent variable to work.
{% endhint %}

That's it - All the tags are now blocked until the user consent. If the user deny cookies in the cmp, the tags will remains blocked.

{% hint style="info" %}
**INFO**

This library is using observer and override CreateElement core JS functionalities. If you expriment tests that are not working, please deactivate Chrome or Firefox extension in your browser.
{% endhint %}

We will add new capabilities regularly (more controls, shared database of tags...)

Any issue or suggestions ? Drop an email at [**support@sfbx.io**](mailto:support@sfbx.io)


# Setting up Google Consent Mode v2 for Google Analytics and Google Ads

Since version 32.7.1 of the CMP, we improved a lot the behavior of the Google Consent Mode v2 in order to be bulletproof regarding the Google requirements.

***

## What is Google Consent Mode v2 ?

Consent mode lets you communicate your user's cookie consent status to Google. Tags adjust their behavior and respect user's choices. Consent mode receives your user's consent choices from your cookie banner or widget and dynamically adapts the behavior of Analytics, Ads, and third-party tags that create or read cookies.

When you enable consent mode, Google measurement products ensure that a visitor’s consent mode state is preserved across the pages they visit.

### **Basic consent mode**

When you implement consent mode in its basic version, you prevent Google tags from loading until a user interacts with a consent banner. This setup transmits no data to Google prior to user interaction with the consent banner. When the user grants consent, Google tags load and execute the consent mode APIs. The tags send the consent states to Google in the following order:

1. Send default consent states.
2. Send updated consent states.

However, when the user doesn’t consent, no data is transferred to Google at all – not even the consent status. Google tags are completely blocked from firing. Consent mode's conversion modeling in Ads is then based on a general model.

### **Advanced consent mode**

When you implement consent mode in its advanced version, Google tags load when a user opens the website or app. The tags load the consent mode API and do the following:

* Set default consent states. By default, consent will be denied, unless you set your own defaults.
* While consent is denied, the Google tags send cookieless pings.
* Wait for user interaction with the banner and update consent states.
* Only when a user grants consent to data collection, Google tags send the full measurement data. Learn more about tag behavior.

This implementation enables improved modeling compared to the Basic one as it provides an advertiser-specific model as opposed to a general model.

Find more information in the [Google official documentation](https://support.google.com/google-ads/answer/10000067?hl=en)

{% hint style="warning" %}
**ATTENTION**

&#x20;In order to leverage Google Consent Mode you need to at least have :

* the Google Advertising Product vendor ( id 755 ) in your source / notice
* and the IAB purposes 1, 3, 4, 7, 9 selected
  {% endhint %}

## Activate Google Consent Mode v2

Go to the backoffice in the Notices menu. Then click on the notice you use or you want to implement in your website.\
Go to Advanced tab and switch on enableGCM

<figure><img src="/files/6R8C3CrwYUMEQswyIPTC" alt=""><figcaption></figcaption></figure>

### Implementation using regular code

Find more information in the [Google official documentation](https://developers.google.com/tag-platform/security/guides/consent?consentmode=advanced)

Now you can get the updated code in the Notice menu. Just click on the given notice.

<figure><img src="/files/L2qLi8q9oJwisfHLqaDK" alt=""><figcaption></figcaption></figure>

1. Click on the **copy** button on the top right of the code window.
2. Then past the code the highest possible just after the `<head>` tag

That's It ! Your website is now ready for Google Consent Mode v2

### Implementation using Google Tag Manager

You can leverage AppConsent CMP using our last template from the GTM Gallery.

1. Install the GTM template **Sfbx AppConsent CMP Setup**
2. Copy/past your APP\_KEY
3. Click on enableGCM in the tag
4. Then deploy.

<figure><img src="/files/0ckkWnDvTaKr83J3w7Nc" alt=""><figcaption></figcaption></figure>

For more information, see this page : [Install AppConsent with Google Tag Manager](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/install-appconsent-with-google-tag-manager)

### Validating the Google Consent Mode v2 using Google Tag Assistant

1. Go to <https://tagassistant.google.com/>
2. Click on Add Domain and fill your domain
3. On your website, accept the CMP then return to tag assistant
4. On the left side, click on the first message entry
5. Now in the middle of the page, select the consent tab, you should have something like below :

<div align="left"><figure><img src="/files/ou4OOFle7n9dSIjfcvev" alt="" width="563"><figcaption></figcaption></figure></div>

If you refuse / deny the CMP , you will get a full denied GCM state :

<div align="left"><figure><img src="/files/z7bHcufmZybHD04D4MRy" alt="" width="563"><figcaption></figcaption></figure></div>


# Utiq integration

This page explains how to integrate Utiq into the CMP.

Utiq is a telecom-operator-powered marketing identifier technology. When enabled on a source, the CMP declares a dedicated Utiq vendor and purpose, displays a Utiq paragraph in layer 1 of the consent banner, and loads the Utiq script from a customer subdomain (`utiq.<domain>` CNAME).&#x20;

***

### 1. Source creation

In the source form, under the **Advanced Settings** tab, you can enable Utiq.&#x20;

<figure><img src="/files/0zirf81tKgG03DPz2D8g" alt=""><figcaption></figcaption></figure>

When enabled, the Utiq global vendor and purpose are automatically added in the source's extra vendors / extra purposes.

<figure><img src="/files/awpGcNbm7TnYv1NgEPap" alt=""><figcaption></figcaption></figure>

#### Utiq Loader

When enabled (default), the CMP automatically injects the Utiq loader script. Disable this option if the Utiq loader is already loaded elsewhere on your website (for example, via Google Tag Manager) to prevent duplicate loading.

#### Utiq domain

It is the domain used to load the Utiq script. Auto-filled from `TargetURL` as `utiq.<main-domain>`.&#x20;

{% hint style="warning" %}
As you can see on [Utiq's onboarding process](https://docs.utiq.com/docs/integration-checklist-onboarding-process), the CNAME utiq.yourdomain.com must be configured by Utiq before enabling this feature - please reach out to your Utiq contact.
{% endhint %}

#### Cookie wall mode

Enable this mode if your site uses a **Consent or Pay** **model**. The [Utiq layer 1 text](#utiq-paragraph-in-layer-1) will include a link to the layer 2, allowing users to refuse Utiq.

***

### 2. Notice creation

In the the **Language & text customization** tab, a field is required when Utiq is enabled.

#### **Data controller for Utiq Privacy Text**&#x20;

<figure><img src="/files/SADIiz1xksskQwt6iRVw" alt=""><figcaption></figcaption></figure>

The value replaces the `[DATA CONTROLLER]` placeholder inside the Utiq paragraph on layer 1. It must match the Data Controller declared on your Privacy Policy page.

#### Utiq paragraph in layer 1

When **Cookie wall mode** is enabled, the Utiq paragraph on layer 1 includes an extra "reject Utiq now" link that takes the user to layer 2 (the purposes screen). From there, the user can leave Utiq set to **FALSE**, accept the rest of the purposes, and save, keeping Utiq refused while still consenting to everything else.

The paragraph also references a `<continue_without_accepting_button_label>` placeholder. Its value is taken from the **"continue without accepting" link** that you configured in the notice.

<figure><img src="/files/7gTGpQUK9BBSQEY4Vu0Z" alt=""><figcaption></figcaption></figure>


# Google GTM

{% content-ref url="/pages/PuXZvj6NkCdB2gwzWItC" %}
[Install AppConsent with Google Tag Manager](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/install-appconsent-with-google-tag-manager)
{% endcontent-ref %}

{% content-ref url="/pages/buXuGWoNPGz2PpxU9JYL" %}
[Manage Google Analytics](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/manage-google-analytics)
{% endcontent-ref %}

{% content-ref url="/pages/1s2PAS9rEp2lNDhGAntt" %}
[Control your Extra vendors finely](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/control-your-extra-vendors-finely)
{% endcontent-ref %}

{% content-ref url="/pages/oM0f2o7YXqadCLcuceHn" %}
[Manage GTM by purposes](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/manage-gtm-by-purposes)
{% endcontent-ref %}

{% content-ref url="/pages/hQVebMLpR218U8fsDsun" %}
[Meta/Facebook pixel](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/meta-facebook-pixel)
{% endcontent-ref %}

{% content-ref url="/pages/rEgV7CT0AxwxUXTxTLr4" %}
[Fixing Google errors 2.1a](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/fixing-google-errors-2.1a)
{% endcontent-ref %}


# Install AppConsent with Google Tag Manager

On this page you will find the method to implement AppConsent CMP with the solution of tag management system "Google Tag Manager"

***

{% hint style="danger" %}
**ATTENTION**

Before proceeding, we assume that you have an installation of GTM on your website.

If you note familiar with Google Tag Manager, please find the official documentation of Google [here](https://support.google.com/tagmanager/answer/6102821?hl=en\&authuser=1)**.**

You can have one CMP implementation per site. If you wish to use Google Tag Manager rather than code-based implementation, make sure you don't have another AppConsent implementation on your site.
{% endhint %}

## Import tag template AppConsent

To import the tag template AppConsent in your Google Tag Manager account :

1. Go to your container
2. Then, in the left-hand side panel, click on **Templates**
3. Then, click on **Search Gallery**

<figure><img src="/files/9rL9lwdDlZWD5WeX6ANu" alt=""><figcaption></figcaption></figure>

4. Then, in the search bar in right-hand side panel, search **Sfbx AppConsent CMP Setup**
5. Find the template named **Sfbx AppConsent CMP Setup**
6. Add it in your container

<figure><img src="/files/h6hnDjnQuyC2NWAh8I1l" alt=""><figcaption></figcaption></figure>

{% hint style="danger" %}
**ATTENTION**

The tag template named **Sfbx - AppConsent CMP - Default Consent Mode** is now deprecated. You no longer need to import it into your container.
{% endhint %}

## Creation and configuration of the tag

To create the tag, you need :

1. Click on **Tags** menu in the left-hand panel
2. Then, click on the New button

<figure><img src="/files/wDXHm48lZj2zUvNWfZPn" alt=""><figcaption></figcaption></figure>

On the configuration display that appears :

1. Name the tag in top left text field
2. Then click on **Tag configuration** section
3. In right-hand side panel, select the tag template **Sfbx AppConsent CMP Setup** in **Custom** section

<figure><img src="/files/qqkRzk1zWTUnc9toyv3h" alt=""><figcaption></figcaption></figure>

Once you have selected the template, add the appkey of the notice and configure the tag to suit your needs.

<figure><img src="/files/z6MGcMrCaylAN3ninvG7" alt=""><figcaption></figcaption></figure>

Finally, you need to tell GTM when to trigger the tag. To do this :

1. Click on **Triggering** section

<figure><img src="/files/R3P8OcjGtxBBWX4aQvcu" alt=""><figcaption></figcaption></figure>

2. Then select the **Consent Initialization - All pages** trigger in the right-hand side panel

<figure><img src="/files/ikxhjAoxK1m5UNzKHFTI" alt=""><figcaption></figcaption></figure>

3. Once the trigger has been added, click **Save to save the tag**


# Manage Google Analytics

Google Analytics management is integrated into the CMP using the GCM (Google Consent Mode) protocol. GCM blocks Google Analytics from depositing cookies or scripts before the user consent. The CMP may continue to block Google Analytics after the user's consent, depending on the user's choices.

On this page you'll find information on how to manage Google Analytics with AppConsent, depending on your implementation.

## Implementing and configuring Google Analytics with GTM

### Measurement ID retrieval

Before implementing Google Analytics, you need to retrieve your measurement ID. To do this, you need :

1. Go to on your Google Analytics dashboard <https://analytics.google.com/>
2. In the left-hand side panel, click on **Admin**

<figure><img src="/files/nlHjUBg6tVQBusEGDPiZ" alt=""><figcaption></figcaption></figure>

3. In the page, find the section **Data collection and modification**
4. Then click on **Data streams** menu

<figure><img src="/files/5XSlPPrXFsLAH3nGi11b" alt=""><figcaption></figcaption></figure>

5. Click on data stream you wish to integrate to open the stream details
6. And copy measurement ID using the button

<figure><img src="/files/o9aphgvaWLQIIBFeHBog" alt=""><figcaption></figcaption></figure>

### Implementing Google Analytics with GTM

1. Return on Google Tag Manager
2. On the left-hand side panel, click on the **Tags** menu
3. Then click on **New** button

<figure><img src="/files/wWooRYJtHb221967aU58" alt=""><figcaption></figcaption></figure>

4. Click on **Tag configuration** section
5. Then in the left-hand side panel, select **Google Analytics**

<figure><img src="/files/YEMPLHPYhWJ5fy4Xsc8G" alt=""><figcaption></figcaption></figure>

6. The click on **Google tag** template

<figure><img src="/files/uFWjiE5pLfmgfTjZrU38" alt=""><figcaption></figcaption></figure>

7. Paste the previously copied ID (see above) into **Tag ID** field

<figure><img src="/files/ZDxiztGBu69j1d2FYtap" alt=""><figcaption></figcaption></figure>

8. In the accordion **Consent Settings** located in **Advanced Settings**, you can see that your tag is already ready to use Google Consent Mode signals

<figure><img src="/files/6TX11W47BZUhTK17JhCO" alt=""><figcaption></figcaption></figure>

9. In triggering section, add the **All Pages** trigger

<figure><img src="/files/tnHYIzF87HBcNgKmm3M9" alt=""><figcaption></figcaption></figure>

10. Then, name and save the tag

## AppConsent configuration for Google Analytics (Google Consent Mode Advanced Mode - GCMv2)

For Google Analytics to be controlled by the CMP, GCM (Google Consent Mode) must be activated in the notice configuration and in the notice implementation (code or GTM).

For more information on implementing Google Consent Mode, see the page: [Setting up Google Consent Mode v2](/configuration/step-3-notice-implementation-web-app-tv/web-cmp/setting-up-google-consent-mode-v2-for-google-analytics-and-google-ads)


# Control your Extra vendors finely

Our CMP relies on the IAB standard to collect and distribute consent to IAB partners. For other partners, we have integrated a system in the configuration interface and in the CMP so that the user can give consent to these non-IAB partners, also known as extra vendors.

***

## Configuration of an extra vendor in the configuration interface

We assume that you have already created your extra vendor in the configuration interface. On your source, add your extra vendor and associate it with IAB purposes or extra purposes.

<figure><img src="/files/KNyFWpFZvKcrkQvgTfNV" alt=""><figcaption></figcaption></figure>

Once configured, save your source and associated notices.

## Configuration in GTMs

### Consent recovery for an extra vendor

The CMP sends an event to the **dataLayer** variable in the GCM (Google Consent Mode) protocol for each extra vendor consented by the user. This event takes the following form:&#x20;

<figure><img src="/files/q0gqAaZiLUBMqeu646sp" alt=""><figcaption></figcaption></figure>

The name of these events is composed of:

* `acextravendor_granted_`
* and the extra vendor id

In GTM, to trigger the extra vendor code, we'll retrieve each event sent by the CMP and trigger the tag.

### Create a trigger for an extra vendor

To retrieve the event sent by the CMP, you need to create a custom trigger. To do this, you must :

1. Go to GTM and click on the **Triggers** menu in the left-hand side panel
2. Click on the **New** button

<figure><img src="/files/gb6PceeZFWOQImHSFRN6" alt=""><figcaption></figcaption></figure>

3. Click on **Trigger configuration**
4. From the list of events in the right-hand side panel, select **Custom event**

<figure><img src="/files/EnZEPPEoiukMjnzxMgon" alt=""><figcaption></figcaption></figure>

5. In the AppConsent configuration interface, go to **Extra Vendors**
6. Then click on the "copy" button on the line of the extra vendor you wish to integrate

<figure><img src="/files/KKQp5tR25I1t2DmvmRbp" alt=""><figcaption></figcaption></figure>

7. Once the partner id has been copied, return to the custom event in GTM
8. In the **Event name** field, enter `acextravendor_granted_` + the partner id

<figure><img src="/files/L3tsAmcV1gwRCWL1HbYf" alt=""><figcaption></figcaption></figure>

9. Name your trigger, then save

{% hint style="danger" %}
**CAUTION**

The **acextravendors\_granted\_id** (in our example **acextravendor\_granted\_rS06k3iu**) must be filled in exactly as shown. You can rename the title (in the top left-hand corner), but never the value in the **Event name** field. Otherwise, you'll lose the link with the tag.
{% endhint %}

### Extra vendor tag creation

1. Click on the **Tags** menu in the left-hand side panel
2. Then click on the **New** button

<figure><img src="/files/PrbbpQLqbeP555YairiI" alt=""><figcaption></figcaption></figure>

3. Click on tag configuration
4. Then select the **Custom HTML** template

<figure><img src="/files/PDbDkbjXVdfSrsrOezmZ" alt=""><figcaption></figcaption></figure>

5. Put the extra vendor's script in the **HTML** field

6. Then click on the **Trigger** section

7. Then select the custom trigger created earlier

<figure><img src="/files/ouepjEDo8WticdI5zNEs" alt=""><figcaption></figcaption></figure>

8. Name and save your tag

{% hint style="danger" %}
**CAUTION**

In this example, we're using the **Custom HTML** template, but it works with other template types.
{% endhint %}

That's it. Now your tag will be fired only if :

* your extra vendor has been consented to by the user
* AND if at least one of the purposes associated with your partner has been consented

Have a suggestion ? Just drop an email to <support@sfbx.io>


# Manage GTM by purposes

This page explains how to trigger a GTM tag according to the processing purposes consented to in the notice by the user. As a publisher, it can be useful to be able to trigger a script without appearing as a non-IAB partner.

The AppConsent CMP automatically triggers 11 events according to the 11 IAB purposes available.

{% hint style="warning" %}
**CAUTION**

If you want to control a non-IAB partner, we recommend you visit this page: [Control your extra vendors](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/control-your-extra-vendors-finely).
{% endhint %}

## Trigger creation

Let's start by adding a custom event.

1. Click on the **Triggers** menu
2. Then on the **New** button

<figure><img src="/files/sRmnhq0JGKadYRzP9LDv" alt=""><figcaption></figcaption></figure>

3. Click on **Trigger configuration**
4. From the list of events in the right-hand side panel, select **Custom event**

<figure><img src="/files/Rj3oYIeVAlTBYS5bcfJ6" alt=""><figcaption></figcaption></figure>

5. In the **Event name** field, enter `appconsent_ctrl_` + the purpose number

<figure><img src="/files/utBUQGE5OqxnJotqnCuX" alt=""><figcaption></figcaption></figure>

6. Name the event as you like, then save.

Depending on your needs, do the same for **appconsent\_ctrl\_2**, **appconsent\_ctrl\_3** ... **appconsent\_ctrl\_10**.

{% hint style="info" %}
**INFO**

As a reminder, here is the list of purposes according to the [IAB Framework](https://iabeurope.eu/iab-europe-transparency-consent-framework-policies/#A_Purposes)**:**

List of purposes :

1. Store and/or access information on a device
2. Use limited data to select advertising
3. Create profiles for personalised advertising
4. Use profiles to select personalised advertising
5. Create profiles to personalise content
6. Use profiles to select personalised content
7. Measure advertising performance
8. Measure content performance
9. Understand audiences through statistics or combinations of data from different sources
10. Develop and improve services
11. Use limited data to select content
    {% endhint %}

## Link the event to a tag

1. Click on the **Tags** menu in the left-hand side panel
2. Then click on the **New** button

<figure><img src="/files/PrbbpQLqbeP555YairiI" alt=""><figcaption></figcaption></figure>

3. Click on Tag Configuration
4. Then select the **Custom HTML** template

<figure><img src="/files/gBJjchIMy9WYmHRkJdKk" alt=""><figcaption></figcaption></figure>

5. Put the extra vendor's script in the **HTML** field

<figure><img src="/files/okL2K3X6bhRwUOlbErMT" alt=""><figcaption></figcaption></figure>

6. Then click on the **Trigger** section
7. Then select the custom trigger created earlier

<figure><img src="/files/FOhPZodgV9EwYRLh4gTp" alt=""><figcaption></figcaption></figure>

8. Name and save your tag

{% hint style="danger" %}
In this example, we're using the **Custom HTML** template, but it works with other template types.
{% endhint %}

That's it. Now your tag will be fired only if :

* If the processing purpose you have selected is consented by the user.

Have a suggestion ? Just drop an email to <support@sfbx.io>


# Meta/Facebook pixel

To manage Facebook pixel consent with AppConsent, we'll create and configure an extra vendor, then trigger a tag containing the Facebook pixel code based on the user's consent.

## Extra vendor Facebook configuration

We assume that you have already created the extra vendor Facebook in the AppConsent configuration interface.

1. Create or edit your source,
2. Add the extra vendor Facebook and **configure it** with the purposes:

* **1** - Store and/or access information on a device
* **3** - Create profiles for personalised advertising
* **4** - Use profiles to select personalised advertising
* **7** - Measure advertising performance

<figure><img src="/files/3DaOqEXHfizaRxzF6sEx" alt=""><figcaption></figcaption></figure>

3. Save your source and all associated notices

## Configuration in Google Tag Manager

To configure your tag according to user consent, see page : [Control your Extra vendors](/configuration/step-3-notice-implementation-web-app-tv/google-gtm/control-your-extra-vendors-finely)


# Fixing Google errors 2.1a

Google as vendor of the TCF, must wait for the CMP to trigger ad request.

But until now, they don't wait for the user consent and throw ad request even if the user is still exposed to the CMP. This is how 2.1a errors come up.

***

### Adsense

Just add this piece of code in your body

```javascript
<script type="text/javascript">
(adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=1;   
__tcfapi('addEventListener', 2, function(tcData, success) {
 if (success && tcData.gdprApplies && (tcData.eventStatus === 'tcloaded' || tcData.eventStatus === 'useractioncomplete') ) {
     (adsbygoogle=window.adsbygoogle||[]).pauseAdRequests=0;
     __tcfapi('removeEventListener', 2, () => {}, tcData.listenerId);  
 }
})
</script>
```

1 - This script will ask Adsense to put on hold adrequest until the cmp collect the consent.

2- After user gave consent, ad requests are fired and ads are displayed.

You should see error 2.1a decreasing quickly. If it not the case, drop us an email to <support@sfbx.io>.

{% hint style="info" %}
**INFO**

Google reports show errors aggregated on 7 days so you need to compare to the sum of Ad calls on the same period if you want to calculate a ratio.
{% endhint %}

**Update on 2020-11-06** : Last clients that used this script have reported a drop of errors by 95% for Adsense.

{% hint style="info" %}
**INFO**

Due to the way all advertising stack are built, it's virtually impossible to get a "zero" errors for the 2.1a errors .

Here is some organic cause of errors :

* Adblockers
* Slow internet bandwidth
* Beta version of browsers
  {% endhint %}


# Android

How to use the SDK ?

The transition to **TCF v2.3** brings significant improvements in terms of transparency for the end user and reliability in the transmission of consent signals to advertising partners (vendors).

**Key points of the update**

* Increased transparency: Improved descriptions of data processing purposes for the user.
* Support for new signals: Optimized management of consent signals for new types of vendors approved by the IAB.
* Interoperability: Ensures that your application remains compatible with the latest requirements from Google and other major advertising platforms (AdTech).

Like all libraries, you'll need to follow a number of steps before using our SDK.

### TargetSdk & CompileSdk

Please note this technical information regarding the configuration of your project.

{% hint style="warning" %}
**Target API Level requirement**<br>

[API level requirements](https://developer.android.com/google/play/requirements/target-sdk)

When you upload your APK, it must meet Google Play's target above *(see link)*.

For this reason, we try to adapt our product as best we can so that it is as up to date as possible, taking advantage of the latest features and updates to the Android APIs.<br>

However, we also strive not to offer a product that imposes these restrictions, and to this end, we follow Google's recommendations by keeping the required targeting level at “minus 1.”<br>

The goal is to continue to take advantage of updates to our product and ensure that your application continues to meet the requirements of the digital marketing industry, while giving you time to migrate.
{% endhint %}

### Google Requirement

This information is only useful if your application targets both children and an older audience.

{% hint style="info" %}
**Privacy policy**

\
It may be necessary to implement an age-neutral screen in your application if it targets both children **AND** an older audience.<br>

**Message from google:**\
Apps that target **both**: **children** AND **older audiences** must not implement APIs or SDKs that are not approved for use in child-directed services unless they are used behind a neutral age screen or implemented in a way that does not result in the collection of data from children.\
\
Here is a list of links to help you understand Google's requirements

* [Google Play Families Policies](https://support.google.com/googleplay/android-developer/answer/9893335?hl=en)
* [Integrate neutral age screen](https://support.google.com/googleplay/android-developer/answer/9867159?hl=en#neutral-agescreen)
* [Manage target audience and app content settings](https://support.google.com/googleplay/android-developer/answer/9867159?hl=en)
* [Google Ads requirements](https://support.google.com/googleplay/android-developer/answer/9893335?hl=en#ads_and_monetization)
* [Understanding Google Play's Families policies](https://playacademy.exceedlms.com/student/catalog/list?category_ids=2558)
  {% endhint %}

<details>

<summary>Expand to get an overview of the CMP</summary>

<img src="/files/2bzKAHDPYZlvSR9dkVe4" alt="" data-size="original"><img src="/files/lL9cCe4lIzlmq0P8JzUN" alt="" data-size="original">

<img src="/files/H9edhgYjj31FkAinj66O" alt="" data-size="original"><img src="/files/HUS4B5aH1ebhS1sHVYmV" alt="" data-size="original">

<img src="/files/fJ0m54qP9m8ACGyxuxi4" alt="" data-size="original">

<img src="/files/5Jvahj6lEiC74s5gcdbj" alt="" data-size="original"><img src="/files/1uUwIEc93h4pSWPf6XjY" alt="" data-size="original">

<img src="/files/5Av8ghAaxtv7LdEFTqMO" alt="" data-size="original"><img src="/files/bEEeAK6Cpm9BLircBReX" alt="" data-size="original">

</details>

#### Let's start at the beginning

{% hint style="success" %}
**TOTAL TIME**

From integration to display *(excluding the settings in your Notice)* - Less than **1 minute** :timer:
{% endhint %}

**1.** Add our maven url to your repositories\
**2.** Add the dependency that corresponds to your platforme\
**3.** Initiate the SDK, display the CMP and voilà!


# Unified SDK


# Step 1: Repository

Dependency Resolution Management

***

## Declaration of the repository

{% tabs %}
{% tab title="Old version" %}
In the `build.gradle` file at the root of your project, add the following:

{% code fullWidth="false" %}

```kotlin
repositories {
    ...
    maven {
        url "https://artifactory.datalf.chat/artifactory/appconsent"
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin DSL" %}
In your `settings.gradle.kts` file, at the root of your project, add the following:&#x20;

```kotlin
dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven { 
            url = uri("https://artifactory.datalf.chat/artifactory/appconsent")
        }
    }
}
```

{% endtab %}

{% tab title="Groovy" %}
In your `settings.gradle` file, at the root of your project, add the following:

```groovy
dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven {
            url "https://artifactory.datalf.chat/artifactory/appconsent"
        }
    }
}
```

{% endtab %}
{% endtabs %}


# Step 2: Dependency

***

Integration of our SDK into your Project

`currentVersion = 6.3.1`

{% tabs %}
{% tab title="Kotlin DSL" %}

#### KotlinDSL - App build.gradle.kts and v**ersion catalog** (gradle/libs.versions.toml) <a href="#kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions" id="kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions"></a>

**Version catalog**[**​**](https://docs.sfbx.io/fr/configuration/notice-implementation/android/dependency#catalogue-de-versions)

```java
[versions]
sfbx = "${currentVersion}"

[libraries]
sfbx = { group = "io.sfbx.appconsent", name = "unifiedsdk", version.ref = "sfbx" }
```

**build.gradle.kts**[**​**](https://docs.sfbx.io/fr/configuration/notice-implementation/android/dependency#buildgradlekts)

```java
dependencies { 
    implementation(libs.sfbx)
}
```

{% endtab %}

{% tab title="Groovy" %}
{% hint style="info" %}
Depending on your project configuration, implement the dependency in your own way
{% endhint %}

**Groovy**  - **App build.gradle**

```groovy
dependencies { 
    implementation 'io.sfbx.appconsent:unifiedsdk:${currentVersion}' 
}
```

{% endtab %}
{% endtabs %}


# Step 3: Integration

***

## How to use AppConsent

### 1. Get your AppKey

On AppConsent configuration interface [https://app.appconsent.io](< https://app.appconsent.io>),  create your unified SDK source.

<figure><img src="/files/MoGc4k3kexUz4EUXOfQg" alt=""><figcaption></figcaption></figure>

Then  create your notice and retrieve your generated **YOUR\_APP\_KEY**&#x20;

<figure><img src="/files/ETbXFzlfMoR29FVrzSmm" alt=""><figcaption></figcaption></figure>

### 2. Initialize AppConsent & use it

The second step is to initialize the SDK and wait for it to be started before using it.

{% hint style="success" %}
**TIP**

We recommend you do this in the **Application** class, Custom **ContentProvider**, **Splashscreen**, or your "**android.intent.category.LAUNCHER**" activity.
{% endhint %}

#### **Why timing your consent prompt matters**

First impressions are critical. Users who encounter a consent dialog before experiencing any value from your app are significantly more likely to dismiss it — or abandon the app entirely.

By initializing the SDK early in the background while strategically delaying the CMP display, you ensure that when the consent prompt appears, users are already engaged. This small timing shift can have a measurable impact on positive consent rates, ad revenue, and long-term retention.

The goal is simple : make consent feel like a natural step in the experience, not a barrier to it.

#### **Technical implementation**

The SDK must be initialized as early as possible in your app's lifecycle. On startup, it bootstraps a WebView and initializes its JavaScript engine — a process that takes time. The earlier you trigger this, the sooner the SDK will be ready.

Initialization does not mean the SDK is immediately usable. You must wait for the `onCmpReady` callback before interacting with it. Only at that point can you safely call `getInstance()` and display the CMP.

{% hint style="info" %}
**INFO**

Even if your ad provider registers with the user consent update events and this is transparent to you, some providers may not work correctly with this solution. For this reason, we recommend that you initialize your provider's SDK only when consent has been given, to ensure that your ads are in accordance with user consent. Think to update it each time the user updates his consent too, thank to callbacks
{% endhint %}

{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.\
The potential **imports** and **visual elements** in these examples are therefore not present, as they are strongly linked to the Android structure and project, which are independent of the SDK.
{% endhint %}

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        SFBX.initialize(
            configuration = Configuration(appkey = "YOUR-APP-KEY"),
            onCmpReady = { appconsent ->
                Log.d(TAG, "Appconsent ready: $appconsent")
            },
            onCmpOnError = { error ->
                Log.e(TAG, "Appconsent init failed", error)
            },
        )
    }
}

class MyActivity : Activity() {
    override fun onCreate() {
        super.onCreate()
        try {
            SFBX.getInstance().presentNotice(
                context = context,
                onCmpDisplayed = {
                    Log.d(TAG, "CMP displayed")
                },
                onCmpNotDisplayed = { error ->
                    Log.d(TAG, "CMP not displayed: $error")
                },
                onCmpClosed = { error ->
                    Log.d(TAG, "CMP closed: $error")
                },
            )
        } catch (error: AppconsentExceptions) {
            Log.w(TAG, "SDK not initialized yet", error)
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        SFBX.initialize(
                new Configuration("YOUR-APP-KEY"),
                appconsent -> Unit.INSTANCE,          // onCmpReady
                error -> {                            // onCmpOnError (initialize never throws)
                    Log.e(TAG, "Appconsent init failed", error);
                    return Unit.INSTANCE;
                }
        );
    }
}

public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            SFBX.getInstance().presentNotice(
                    context,
                    () -> Unit.INSTANCE,    // onCmpDisplayed
                    error -> Unit.INSTANCE, // onCmpNotDisplayed (error may be non-null)
                    error -> Unit.INSTANCE  // onCmpClosed (error non-null on failure)
            );
        } catch (AppconsentExceptions error) {
            Log.w(TAG, "SDK not initialized yet", error);
        }
    }
}
```

{% endtab %}
{% endtabs %}

#### **Best Practices**

Waiting for the right moment to display the CMP is just as important as initializing the SDK early. Here are a few recommended triggers :

* **Before the first ad load** — ensures consent is collected before any ad request is made, keeping you compliant by design.
* **After the onboarding flow** — the user has already experienced value, making them more likely to engage positively with the consent prompt.
* **At a natural pause in the user journey** — such as between two screens or after a key action, where an interruption feels least disruptive.

{% hint style="danger" %}
Avoid displaying the CMP on the very first screen of your app. Users who haven't yet experienced any value are significantly more likely to dismiss or ignore the prompt.
{% endhint %}

### 3. Suggest CMP to your users

It is also advisable to provide your users with an entry allowing them to view and modify their consent, such as a configuration screen.

{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.\
The potential **imports** and **visual elements** in these examples are therefore not present, as they are strongly linked to the Android structure and project, which are independent of the SDK.
{% endhint %}

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        findViewById<View>(R.id.button_display_privacy_policy).setOnClickListener {
            try {
                SFBX.getInstance()
            } catch (e: Exception) {
                null
            }?.displayLayer2(
                this@SecondActivity,
                onCmpDisplayed = {
                    // The CMP is currently being displayed to the user.
                },
                onCmpNotDisplayed = { appconsentExceptions ->
                    // The CMP did not display for some reason.
                },
                onCmpClosed = { appconsentExceptions ->
                    // The CMP screen has been closed.
                },
            )
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        findViewById(R.id.button_display_privacy_policy).setOnClickListener(v -> {
            try {
                SFBX.getInstance().displayLayer2(
                    this,
                    () -> {
                        // The CMP is currently being displayed to the user.
                        return Unit.INSTANCE;
                    },
                    (appconsentExceptions) -> {
                        // The CMP did not display for some reason.
                        return Unit.INSTANCE;
                    },
                    (appconsentExceptions) -> {
                        // The CMP screen has been closed.
                        return Unit.INSTANCE;
                    }
                );
            } catch (AppconsentExceptions e) {
                // Something goes wrong and the SDK returns a known exception
            }
        });
    }
}
```

{% endtab %}
{% endtabs %}


# How to use it ?

**How to use Appconsent Instance ?**

Here is a list of the most commonly used methods.

### Knowing whether the user has given consent

```kotlin
appconsent.isConsentGiven()
```

Returns `true` if consent has been given, `false` otherwise.

{% hint style="danger" %}
**WARNING**

Please note that this only refers to whether the user **has confirmed a choice** and not whether they have accepted or refused their consent.
{% endhint %}

### CMP display modes

There are three display modes:

1. The first mode is **ONLY** executed if user consent is required.
2. The second mode allows your users to view/modify their consent with direct access to layer 1 (*often used from your settings screen to display your user's privacy policy*) - layer 1 corresponds to the main display of your notice. The window will be displayed **ALL THE TIME**.\
   ***This does not take into account the GDPR regulations of your users' geographic location.***
3. The last one is actually equivalent to the second one, except that the user has direct access to layer 2. This screen allows you to configure your different purposes/stacks in detail, as well as your partners. The window will be displayed **ALL THE TIME**.\
   ***This does not take into account the GDPR regulations of your users' geographic location.***

{% hint style="info" %}
**NOTE**

By default, this “**presentNotice**” method attempts to display the CMP. It attempts to do so because, depending on the region of your users, it will follow its controls and display the CMP only if necessary.\
\
It will also be displayed if user consent has not yet been given or if it needs to be renewed.
{% endhint %}

```kotlin
appconsent.presentNotice()  // Conditional — displays only if GDPR applies, and consent is missing or no longer valid
appconsent.displayLayer1()  // GDPR-agnostic — always displays the first consent layer
appconsent.displayLayer2()  // GDPR-agnostic — always displays the second consent layer
```

### **GCM Status**

{% hint style="info" %}
**INFO**

This method shows the **current** status of GCMv2 (Google Consent Mode V2).

Before calling up this method, it's best to make sure that the user has **already given his consent** and that it's up to date, and that the CMP doesn't need to be redisplayed.

Otherwise :

* either the saved value of the **old consent** will be returned
* or the **default values** of your FirebaseAnalytics AndroidManifest configuration will be returned

[Set the default consent state from google documentation](https://developers.google.com/tag-platform/security/guides/app-consent?consentmode=advanced\&platform=android#default-consent)
{% endhint %}

```kotlin
appconsent.getGCMState()
```

Return `ACGcmState`

<details>

<summary>Data model representing the state of GCMv2 (Google Consent Mode v2)</summary>

This will allow you to define consent from your Firebase Analytics instance.

`isAnalyticsStorageGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#ANALYTICS\_STORAGE**

`isAdStorageGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#AD\_STORAGE**

`isAdUserDataGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#AD\_USER\_DATA**

`isAdPersonalizationGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#AD\_PERSONALIZATION**

[FirebaseAnalytics.ConsentType](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.ConsentType)

</details>

### **Check for update**

This method allows you to check from our servers whether your Notice has been updated since it was last displayed on your user's device.

{% hint style="info" %}
**INFO**&#x20;

The method will return true if you have modified the Source and/or Notice from your dashboard and, if and **only if**, you have configured your Notice to update for all your users.
{% endhint %}

<figure><img src="/files/9qusg58AojkqxJkrT4ZL" alt=""><figcaption></figcaption></figure>

{% hint style="danger" %}
**ATTENTION**

This method can only be used once within a **predefined time period**. This allows you to force a first network call to make sure your users are up to date. The next calls to this method will use the cache of the previous response or attempt a network call if the previous one was in error.
{% endhint %}

```kotlin
appconsent.checkForUpdate({ needToUpdate: Boolean ->
    if (needToUpdate) {
        /*
         The Notice has been updated and must be resubmitted to your users.

         We recommend clearing the existing consent locally, then letting your
         app's natural flow re-trigger the CMP (e.g. via presentNotice() called
         at your usual checkpoint, as set up earlier in your app lifecycle).

         Clearing the consent locally avoids an unnecessary network call on the
         next launch to check whether the Notice has been updated, since no
         consent will be present on the user's device.
         */
        appconsent.askToClearConsent(
            onSuccess = { },
            onError = { appconsentExceptions -> },
        )

        // Do not call presentNotice() directly here.
        // Instead, redirect the user to the appropriate screen in your app
        // where the CMP will be displayed as part of the normal user journey.

    } else {
        /*
         The Notice has not changed since the last verification — no changes
         have been made on your end, and no internal updates have been applied
         (such as a vendor list update).
         No action is required.
         */
    }

}) { appconsentExceptions: AppconsentExceptions ->
    // An error occurred while checking for Notice updates.
}
```

## Using AppConsent's more specific methods

Here's a list of methods that could be useful if you want to go further in tracking user consent.

### **IAB Purpose X Allowed**

```kotlin
appconsent.isIabPurposeAllowed(1)
```

Returns `true` if the purpose with `id = 1` is authorized, `false` otherwise. The `id` to pass is the `iabId` of your purpose.

### IAB Purposes Allowed

```kotlin
appconsent.iabPurposeIdsAllowed() : List<Int>
```

Returns a list of integers representing the IDs of **accepted** purposes **ONLY**.

### **IAB Feature X Allowed**

```kotlin
appconsent.isIabFeatureAllowed(1)
```

Returns `true` if the feature with `id = 1` is authorized, `false` otherwise. The `id` to pass is the `iabFeatureId` of your feature.

### IAB Features Allowed

```kotlin
appconsent.iabFeatureIdsAllowed() : List<Int>
```

Returns a list of integers representing the IDs of **accepted** features **ONLY**.

### **IAB Special Feature X Allowed**

```kotlin
appconsent.isIabSpecialFeatureAllowed(1)
```

Returns `true` if the special feature with `id = 1` is authorized, `false` otherwise. The `id` to pass is the `iabSpecialFeatureId` of your special feature.

### IAB Special Features Allowed

```kotlin
appconsent.iabSpecialFeatureIdsAllowed() : List<Int>
```

Returns a list of integers representing the IDs of **accepted** special features **ONLY**.

### **IAB Special Purpose X Allowed**

```kotlin
appconsent.isIabSpecialPurposeAllowed(1)
```

Returns `true` if the special purpose with `id = 1` is authorized, `false` otherwise. The `id` to pass is the `iabSpecialPurposeId` of your special purpose.

### IAB Special Purposes Allowed

```kotlin
appconsent.iabSpecialPurposeIdsAllowed() : List<Int>
```

Returns a list of integers representing the IDs of **accepted** special purposes **ONLY**.

### **Stack X Allowed**

```kotlin
appconsent.isIabStacksAllowed(1)
```

Returns `true` if the stack with `id = 1` is authorized, `false` otherwise. The `id` to pass is the `iabStackId` of your stack.

### IAB Stacks Allowed

```kotlin
appconsent.iabStacksIdsAllowed() : List<Int>
```

Returns a list of integers representing the IDs of **accepted** stack **ONLY**.

### **Vendor X Allowed**

```kotlin
appconsent.isIabVendorAllowed(1)
```

Returns `true` if the vendor with `id = 1` is authorized, `false` otherwise. The `id` to pass is the `iabVendorId` of your stack.

### IAB Vendors Allowed

```kotlin
appconsent.iabVendorsAllowed() : List<Int>
```

Returns a list of integers representing the IDs of **accepted** vendors **ONLY**.

### **Extra Vendor X Allowed**

```kotlin
appconsent.isExtraVendorAllowed("abc123")
```

Returns `true` if the extra vendor with `id = "abc123"` is authorized, `false` otherwise.

### Extra Vendors **Allowed**

```kotlin
appconsent.extraVendorsAllowed() : List<String>
```

Returns a list of string representing the IDs of **accepted** extra vendors **ONLY**.

### **Extra Purpose X Allowed**

```kotlin
appconsent.isExtraPurposeAllowed("abc123")
```

Returns `true` if the extra purpose with `id = "abc123"` is authorized, `false` otherwise.

### Extra **Purpose** **Allowed**

```kotlin
appconsent.extraPurposeIdsAllowed() : List<String>
```

Returns a list of string representing the IDs of **accepted** extra purpose **ONLY**.

### Purposes list

```kotlin
appconsent.getPurposes() : List<ACConsentable>
```

Returns a list of all purposes in your source with the user-defined status `ACConsentable`.

```kotlin
data class ACConsentable(
    override val id: String,
    override val type: String,
    override val status: String,
    override val additionalInfo: Map<String, String>,
)
```

In this example, the `id` is that of the purpose, the `type` is PURPOSE, the `status` is an integer in string form (see `ACConsentableStatus`), and `additionalInfo` will allow us to retrieve the latest updates from the IAB in the future if it adds any.\
It's time for our SDK to offer the new entry.

### Purposes list by status

```kotlin
appconsent.getPurposesByStatus(status: ACConsentableStatus) : List<ACConsentable>
```

Returns a list of all purposes in your source filtered by the status you pass as a parameter.

### Features list

```kotlin
appconsent.getFeatures() : List<ACConsentable>
```

Returns a list of all features in your source with the user-defined status `ACConsentable`.

### Features list by status

```kotlin
appconsent.getFeaturesByStatus(status: ACConsentableStatus) : List<ACConsentable>
```

Returns a list of all features in your source filtered by the status you pass as a parameter.

### Special Purpose list

```kotlin
appconsent.getSpecialPurposes() : List<ACConsentable>
```

Returns a list of all special features in your source with the user-defined status `ACConsentable`.

### Special Purpose list by status

```kotlin
appconsent.getSpecialPurposesByStatus(status: ACConsentableStatus) : List<ACConsentable>
```

Returns a list of all special purpose in your source filtered by the status you pass as a parameter.

### Special Feature list

```kotlin
appconsent.getSpecialFeatures() : List<ACConsentable>
```

Returns a list of all special features in your source with the user-defined status `ACConsentable`.

### List de Special Feature par status

```kotlin
appconsent.getSpecialFeaturesByStatus(status: ACConsentableStatus) : List<ACConsentable>
```

Returns a list of all special features in your source filtered by the status you pass as a parameter.

### Extra Purpose list

```kotlin
appconsent.getExtraPurposes() : List<ACConsentable>
```

Returns a list of all extra purpose in your source with the user-defined status `ACConsentable`.

### Extra Purpose list by status

```kotlin
appconsent.getExtraPurposesByStatus(status: ACConsentableStatus) : List<ACConsentable>
```

Returns a list of all extra purpose in your source filtered by the status you pass as a parameter.

### Stack list

```kotlin
appconsent.getStacks() : List<ACConsentable>
```

Returns a list of all stack in your source with the user-defined status `ACConsentable`.

### Stack list par status

```kotlin
appconsent.getStacksByStatus(status: ACConsentableStatus) : List<ACConsentable>
```

Returns a list of all stack in your source filtered by the status you pass as a parameter.

### Extra Vendors list

```kotlin
appconsent.getExtraVendors() : List<ACExtraVendor>
```

Returns a list of all extra vendors in your source with the user-defined status `ACExtraVendor`.

```kotlin
data class ACExtraVendor(
    override val id: String,
    override val type: String,
    override val status: String,
    override val additionalInfo: Map<String, String>,
)
```

### Extra Vendors list by status

```kotlin
appconsent.getExtraVendorsByStatus(status: ACConsentableStatus) : List<ACExtraVendor>
```

Returns a list of all extra vendors in your source filtered by the status you pass as a parameter.

### Is the user's consent mixed?

```kotlin
appconsent.isConsentMixed()
```

Returns `true` if all user consent is mixed. That is, if some consent items have been accepted and others rejected.

### Did the user accept everything?

```kotlin
appConsent.isAllAllowed()
```

Returns `true` if all consentables, stacks, and vendors are accepted. Returns `false` if at least one of them is not accepted.

### Did the user refuse everything?

```kotlin
appConsent.isAllDisallowed()
```

Returns `true` if all consentables, stacks, and vendors are rejected. Returns `false` if at least one of them is not rejected.

### Define the status of consentable items

```kotlin
appconsent.setConsentableConsents(
    newConsentableConsents: ACConsentOverride,
    onSuccess: () -> Unit,
    onError: (appconsentExceptions : AppconsentExceptions) -> Unit,
)
```

Define the status of the new consent and save it.

The `ACConsentOverride` object allows you to modify any consentable item as many times as you want in a single operation. It provides a list of consentable items to be modified, such as purposes, stacks, vendors, etc.

Each status is constructed in the following form using the `ACConsentStatus` object.\
The latter takes the following parameters:

* **id**: The ID of the consentable you want to modify (as a string)
* **status**: the new status you want to “redefine” (as a boolean)
  * `true`: **ALLOWED**
  * `false`: **DISALLOWED**
* **legintStatus**: the new legitimate status you want to “redefine” (as a boolean)
  * `true`: **ALLOWED**
  * `false`: **DISALLOWED**

`onSuccess`**:** is the callback that will be called if consent has been successfully modified and saved

`onError`**:** is the callback that will be called if an error occurred during the modification

### **Delete the consent**

Completely resets the user's consent status.

```kotlin
appConsent.askToClearConsent(
    onSuccess: () -> Unit,
    onError: (appconsentExceptions : AppconsentExceptions) -> Unit,
)
```

{% hint style="info" %}
This action is asynchronous.
{% endhint %}

This action deletes all locally stored consent information for the user\
(e.g., choices for vendors, purposes, etc.). After this method is called, the SDK\
will consider the user as not having provided consent yet. The consent notice (CMP) will be displayed again the next time an action requiring consent is performed.

{% hint style="info" %}
**@param** onSuccess A callback invoked upon successful update.\
**@param** onError A callback invoked if the update fails.
{% endhint %}

### Define your external IDs

```kotlin
val ids = mapOf<String, String>("customPersonalId" to "abze23", "otherData" to "{\"name\": \"test\"}")
appConsent.askToSetExternalIds(
    externalIds = ids,
    onSuccess = { },
    onError = { appconsentExceptions -> }
)
```

Allows you to set your external credentials while saving them on our servers.

### Retrieve your external IDs

Retrieve your previously saved external IDs

```kotlin
appConsent.askToGetExternalIds(
    onSuccess = { externalIds -> },
    onError = { appconsentExceptions -> }
)
```

### Define your external IDs locally

```kotlin
val ids = mapOf<String, String>("customPersonalId" to "abze23", "otherData" to "{\"name\": \"test\"}")
appConsent.setLocalExternalIds(
    externalIds = ids
)
```

Allows you to save your external credentials **LOCALLY**, which **will be sent** to the server when the user gives their consent.

### Retrieve your local external IDs

```kotlin
appConsent.getLocalExternalIds() : Map<String, String>
```

Retrieve your externally saved IDs locally

### Get User ID

```kotlin
appconsent.getUserId() : String
```

Retrieves the user ID used for consent

### Find out if the user has restricted the use of their advertising ID

```kotlin
appconsent.isLimitedTrackingEnabled() : Boolean
```

`true` if the user ID is restricted, `false` otherwise

### Knowing if your user is subject to consent

```kotlin
appconsent.isSubjectToGDPR() : Boolean?
```

Returns `true` if your user is subject to consent, `false` if the user is not, `null` if the information is not yet known by our SDK.

### Know the overall status of purposes

```kotlin
appconsent.getPurposesStatus() : ACConsentableStatus
```

Allows you to quickly see the status of all purposes for your source, following user consent.

```kotlin
enum class ACConsentableStatus{
    DISALLOWED,
    PENDING,
    ALLOWED,
    MIXED,
    UNKNOWN,
}
```

### Know the overall status of stacks

```kotlin
appconsent.getStacksStatus() : ACConsentableStatus
```

Allows you to quickly see the status of all stacks for your source, following user consent.

### Know the overall status of vendors

```kotlin
appconsent.getVendorsStatus() : ACConsentableStatus
```

Allows you to quickly see the status of all vendors for your source, following user consent.

### Know the overall status of user's consent

```kotlin
appconsent.getConsentStatus() : ACConsentableStatus
```

Provides a quick overview of the status of user consent.

Quickly see whether the user has **ACCEPTED ALL**, **REFUSED ALL**, **MIXED**, **PENDING**, or **UNKNOWN**.

### Define user consent to REFUSE ALL

```kotlin
appconsent.forceDenyAll(onSuccess: () -> Unit, onError: (appconsentExceptions : AppconsentExceptions) -> Unit)
```

Allows you to confirm the “**DECLINE ALL**” action. This action behaves in the same way as if the CMP were displayed to your users and they clicked on the “**decline all**” button.

A new consent will be applied and saved.

### Define user consent to ACCEPT ALL

```kotlin
appconsent.forceAcceptAll(onSuccess: () -> Unit, onError: (appconsentExceptions : AppconsentExceptions) -> Unit)
```

Allows you to validate the “**ACCEPT ALL**” action. This action behaves in the same way as if the CMP were displayed to your users and they clicked on the “**accept all**” button.

A new consent will be applied and saved.

### Save your floating purpose

```kotlin
appconsent.saveFloatingPurpose(
    floatingPurposesStatus: Map<String, Boolean>,
    onSuccess: () -> Unit,
    onError: (appconsentExceptions : AppconsentExceptions) -> Unit
)
```

If you have defined floating purposes in your source, define their identifiers and user statuses as parameters so that they are taken into account and saved.

### Find out if any of your floating purposes need to be updated

```kotlin
appconsent.isFloatingNeedUpdate(
    floatingPurposeId: String,
    onSuccess: (isFloatingNeedToUpdate: Boolean) -> Unit,
    onError: (appconsentExceptions : AppconsentExceptions) -> Unit
)
```

If you have defined floating purposes in your source, this method will check whether the one passed as a parameter has been updated since then.

`onSuccess`: will return `true` or `false` depending on

### Forces the saving of your external credentials

```kotlin
appconsent.saveExternalIds(
    onSuccess: () -> Unit,
    onError: (appconsentExceptions : AppconsentExceptions) -> Unit
)
```

Allows you to save your external credentials on the server.

## (Bonus) Retrieve your consents

Your consents are saved in `SharedPreferences` of your application. To know more about keys used to save your consents, please refer to the [**IAB documentation**](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details).

We also provide an additional key for Google Additionnal Consent `IABTCF_AddtlConsent` returning a String.


# Git sample

A ready-to-run Android project showcasing a full integration of the UnifiedSDK.

## 🎯 Purpose

This project is a complete Android application that demonstrates how to integrate the **UnifiedSDK** in a real-world scenario. It gives you a quick overview of the key steps for a successful integration: initialization, configuration, and usage of the SDK's main features.

***

## 📦 What's included

* SDK initialization at app startup
* Required dependency setup
* Sample API calls using the SDK
* Callback and error handling

***

## 🚀 Quick start

### **1. Clone the repository**

```bash
git clone https://gitlab.datalf.chat/customers/unifiedsdk-sample.git
```

### **2. Open in Android Studio**

Open Android Studio → **File > Open** → select the cloned folder.

### **3. Sync and run**

Let Gradle sync the dependencies, then run the app on an emulator or a physical device.

***

## 🔗 Repository access

👉 [**View the project on GitLab**](https://gitlab.datalf.chat/customers/unifiedsdk-sample) — Clone, fork, or browse the source code directly.

***

## 📸 Preview

<figure><img src="/files/YyZOAQtw6T3vrHbZLnWT" alt="A screenshot of the project structure in Android Studio"><figcaption><p>project structure in Android Studio</p></figcaption></figure>

<figure><img src="/files/mHUkDvmMz1pfLxNx6x6T" alt="A screenshot of the running app (main screen)"><figcaption><p>Running app (main screen)</p></figcaption></figure>

<figure><img src="/files/LiYvf3YoFuYQkWIwwQhE" alt="A screenshot of the console/logs showing the SDK successfully initialized"><figcaption><p>console/logs showing the SDK successfully initialized</p></figcaption></figure>

***

## 💡 Tips

{% hint style="info" %}
Make sure you have completed **Step 1: Repository** and **Step 2: Dependency** before running the sample, so that all dependencies resolve correctly.
{% endhint %}


# Migrate from 0.3.0-beta01 to 6.0.0

## Impact of Migration

Version **0.3.0-beta01** was a release that paved the way for version **6.0.0**.

During the development of version **6.0.0**, improvements were made and some breaking changes were implemented.

## What are the implications for your application?

There are two major changes in this new version.

### The SDK Configuration

The first is the “**io.sfbx.appconsent.unifiedsdk.Configuration**” object.\
The **forceGdpr** field has been removed (*this field was no longer in use—therefore, there is no impact on SDK usage*).

### The initialize callback onError

The **onCmpOnError** callback in the SDK’s **initialization method**

```kotlin
public final fun initialize(
context: Context,
configuration: Configuration,
onCmpReady: (Appconsent) -> Unit,
onCmpOnError: (AppconsentExceptions) -> Unit
): Unit
```

A parameter has been added to the callback to provide more information about the type of error that occurred during the initialization phase

### AppconsentExceptions - The new sealed class

This new sealed class implements various subclasses of **RuntimeException**, providing a wealth of information about any exceptions that may occur while using the SDK.

{% hint style="warning" %}
:rotating\_light: Since this class is a **sealed class**, it is important to note that if you EXPLICITLY list all subclasses in a \`**when**\` clause, your code may no longer compile when a new version of the SDK is released.

The compiler will then inform you that you must implement the new class, or display a message stating that one of the exceptions cannot be found if it has been removed from the SDK.
{% endhint %}

We recommend that you explicitly implement exception handling so that you can, for example, stop the SDK initialization or retry it.

In the case of an exception of type: **AppConsentWebviewError**, this error will inform you that the webview used to process your consent could not be instantiated or is not fully functional.

{% hint style="info" %}
*For example, it’s possible that one of your users’ devices doesn’t have a WebView, or that at some point the module was disabled or is currently being updated. In this case, the SDK won’t be able to initialize until the module is available again.*
{% endhint %}

An exception of type **AppConsentAppKeyXXX** will indicate a problem with your **AppKey**; please review the error and contact support if necessary.

### What to do if an error occurs

Try calling the **SFBX.initialize(...)** method again, and if it still returns an error, stop using it until the next time you start your application.


# Migrate from Old SDK

### Updating the old repository

Formerly we had:

```kotlin
maven { 
  url = uri("https://artifactory.datalf.chat/artifactory/app-consent-v2-release")
}
```

From now on, it is necessary to:

```kotlin
maven { 
  url = uri("https://artifactory.datalf.chat/artifactory/appconsent")
}
```

### Implementation change

Previously, we had:

```kotlin
dependencies { 
    implementation("com.sfbx.appconsent:appconsent-ui-v3:X.Y.Z")
}
```

From now on, you must:

```kotlin
dependencies { 
    implementation("io.sfbx.appconsent:unifiedsdk:X.Y.Z")
}
```

### Change in integration

#### Imports

Before we had:

```kotlin
import com.sfbx.appconsentv3.*
```

Now the imports are as follows:

```kotlin
import io.sfbx.appconsent.unifiedsdk.*
```

#### Initializing the SDK

This is very similar to the old SDK (like many other libraries).

Before, we had:

1. Initialization of the SDK
2. Registration of callbacks to intercept the user's response
3. Attempt to display the CMP
4. Deletion of callbacks regardless of the result

```kotlin
AppConsentSDK.initialize(
  appKey = "YOUR_APP_KEY"
) { appConsentInitialized ->
  appConsentInitialized.setOnPresentNoticeListener(object : OnPresentNoticeListener {
    override fun presentConsentError(error: Throwable?) {
      appConsentInitialized.setOnPresentNoticeListener(null)
    }

     override fun presentConsentGiven() {
       appConsentInitialized.setOnPresentNoticeListener(null)
     }
  })
  
  if(false == appConsentInitialized.tryToDisplayNoticeFromUiContext(uiContext = this@MainActivity, force = false)) {
    appConsentInitialized.setOnPresentNoticeListener(null)
  }
}
```

Now :

1. Initializing the SDK
2. Display request (the display & consent response is returned directly in the method callbacks)

```kotlin
SFBX.initialize(
  context = this@MyActivity.context,
  configuration = Configuration(appkey = "YOUR-APP-KEY"),
  onCmpReady = { appconsent ->
    appconsent.presentNotice(
      context = context,
      onCmpDisplayed = { },
      onCmpNotDisplayed = { },
      onCmpClosed = { },
    )},
  onCmpOnError = { appconsentExceptions ->
     },
)
```

### Displaying the consent screen to your users

This allows your users to view their consent and modify it if necessary.

Before, we had:

```kotlin
AppConsentSDK.getInstance()
  ?.setOnPresentNoticeListener(object : OnPresentNoticeListener {
    override fun presentConsentError(error: Throwable?) {
      AppConsentSDK.getInstance()?.setOnPresentNoticeListener(null)
    }

    override fun presentConsentGiven() {
      AppConsentSDK.getInstance()?.setOnPresentNoticeListener(null)
    }
  })

  val isCmpDisplayed =
    AppConsentSDK.getInstance()?.tryToDisplayNotice(force = true) ?: false
  if (false == isCmpDisplayed) {
    AppConsentSDK.getInstance()?.setOnPresentNoticeListener(null)
  }
```

Now :

```kotlin
try {
    SFBX.getInstance()
} catch (_: Exception) {
    null
}?.displayLayer2(context,
    onCmpDisplayed = { },
    onCmpNotDisplayed = { },
    onCmpClosed = { },
)


// Alternatively, the following approach is more idiomatic Kotlin,
// using runCatching to handle the exception in a functional style.


runCatching {
    SFBX.getInstance()
}.onSuccess { appconsent ->
    appconsent.displayLayer2(context,
        onCmpDisplayed = { },
        onCmpNotDisplayed = { },
        onCmpClosed = { },
    )
}.onFailure {
}
```


# Old SDK (AppConsentSDK)


# Step 1: Repository

Dependency Resolution Management

***

## Declaration of the repository

{% tabs %}
{% tab title="Old version" %}
In the `build.gradle` file at the root of your project, add the following:

{% code fullWidth="false" %}

```kotlin
repositories {
    ...
    maven {
        url "https://artifactory.datalf.chat/artifactory/app-consent-v2-release"
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin DSL" %}
In your `settings.gradle.kts` file, at the root of your project, add the following:&#x20;

```kotlin
dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven { 
            url = uri("https://artifactory.datalf.chat/artifactory/app-consent-v2-release")
        }
    }
}
```

{% endtab %}

{% tab title="New Groovy" %}
In your `settings.gradle` file, at the root of your project, add the following:

```groovy
dependencyResolutionManagement {
    ...
    repositories {
        ...
        maven {
            url "https://artifactory.datalf.chat/artifactory/app-consent-v2-release"
        }
    }
}
```

{% endtab %}
{% endtabs %}


# Step 2: Dependency

***

Depending on your platform (Smartphone/Tablet or TV), integrate the Clear or TV version.

`currentClearVersion = 5.9.0`\
`currentTvVersion = 5.9.0`

{% tabs %}
{% tab title="Mobile / Tablet" %}
{% hint style="info" %}
Depending on your project configuration, implement the dependency in your own way
{% endhint %}

**Groovy**  - **App build.gradle**

```groovy
dependencies { 
    implementation 'com.sfbx.appconsent:appconsent-ui-v3:${currentClearVersion}' 
}
```

#### KotlinDSL - App build.gradle.kts without v**ersion catalog** <a href="#kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions" id="kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions"></a>

```kotlin
dependencies { 
    implementation("com.sfbx.appconsent:appconsent-ui-v3:${currentClearVersion}")
}
```

#### KotlinDSL - App build.gradle.kts and v**ersion catalog** (gradle/libs.versions.toml) <a href="#kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions" id="kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions"></a>

**Version catalog**[**​**](https://docs.sfbx.io/fr/configuration/notice-implementation/android/dependency#catalogue-de-versions)

```java
[versions]
sfbx = "${currentClearVersion}"

[libraries]
sfbx = { group = "com.sfbx.appconsent", name = "appconsent-ui-v3", version.ref = "sfbx" }
```

**build.gradle.kts**[**​**](https://docs.sfbx.io/fr/configuration/notice-implementation/android/dependency#buildgradlekts)

```java
dependencies { 
    implementation(libs.sfbx)
}
```

{% endtab %}

{% tab title="TV" %}
{% hint style="info" %}
Depending on your project configuration, implement the dependency in your own way
{% endhint %}

**Groovy**  - **App build.gradle**

```groovy
dependencies { 
    implementation 'com.sfbx.appconsent:appconsent-tv:${currentTvVersion}' 
}
```

#### KotlinDSL - App build.gradle.kts without v**ersion catalog** <a href="#kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions" id="kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions"></a>

```kotlin
dependencies { 
    implementation("com.sfbx.appconsent:appconsent-tv:${currentTvVersion}")
}
```

#### KotlinDSL - App build.gradle.kts and v**ersion catalog** (gradle/libs.versions.toml) <a href="#kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions" id="kotlindsl---app-buildgradlekts-sans-le-catalogue-de-versions"></a>

**Catalogue de versions**[**​**](https://docs.sfbx.io/fr/configuration/notice-implementation/android/dependency#catalogue-de-versions)

```java
[versions]
sfbx = "${currentTvVersion}"

[libraries]
sfbx = { group = "com.sfbx.appconsent", name = "appconsent-tv", version.ref = "sfbx" }
```

**build.gradle.kts**[**​**](https://docs.sfbx.io/fr/configuration/notice-implementation/android/dependency#buildgradlekts)

```java
dependencies { 
    implementation(libs.sfbx)
}
```

{% endtab %}
{% endtabs %}


# Step 3: Integration

***

## How to use AppConsent

### 1. Get your AppKey

The first step is to create your source / notice and retrieve your generated **YOUR\_APP\_KEY** from AppConsent :[ https://app.appconsent.io](< https://app.appconsent.io>)

<figure><img src="/files/ETbXFzlfMoR29FVrzSmm" alt=""><figcaption></figcaption></figure>

### 2. Initialize AppConsent & use it

The second step is to initialize the SDK and wait for it to be started before using it.

{% hint style="success" %}
**TIP**

We recommend you do this in the **application** class, **splashscreen**, or your "**android.intent.category.LAUNCHER**" activity.
{% endhint %}

The idea is to be able to display the CMP as soon as possible, so that you can load your advertising supplier.

{% hint style="info" %}
**INFO**

Even if your ad provider registers with the user consent update events and this is transparent to you, some providers may not work correctly with this solution. For this reason, we recommend that you initialize your provider's SDK only when consent has been given, to ensure that your ads are in accordance with user consent. Think to update it each time the user updates his consent too, thank to callbacks
{% endhint %}

{% tabs %}
{% tab title="Mobile / Tablet" %}
{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.\
The potential **imports** and **visual elements** in these examples are therefore not present, as they are strongly linked to the Android structure and project, which are independent of the SDK.
{% endhint %}

**Kotlin**

```kotlin
import com.sfbx.appconsentv3.AppConsent
import com.sfbx.appconsentv3.ui.AppConsentSDK
import com.sfbx.appconsentv3.ui.listener.OnPresentNoticeListener

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        AppConsentSDK.initialize(
            appKey = "YOUR_APP_KEY"
        ) { appConsentInitialized ->
            
            // Set a callback to let you know when the user has completed validation
            registerCallback(appConsentInitialized)
            
            // Once loaded, try to display CMP if needed
            if(false == appConsentInitialized.tryToDisplayNoticeFromUiContext(uiContext = this@MainActivity, force = false)) {
                appConsentInitialized.setOnPresentNoticeListener(null)
            }
        }
    }

    private fun registerCallback(appConsent: AppConsent){
        appConsent.setOnPresentNoticeListener(object : OnPresentNoticeListener {

             override fun presentConsentError(error: Throwable?) {
                // remove listener
                appConsent.setOnPresentNoticeListener(null)
                    
                // Load Ads providers
                ...
            }

            override fun presentConsentGiven() {
                // remove listener
                appConsent.setOnPresentNoticeListener(null)

                // Load Ads providers
                ...
            }
        })
    }
}
```

**Java**

```java
import com.sfbx.appconsentv3.AppConsent;
import com.sfbx.appconsentv3.ui.AppConsentSDK;
import com.sfbx.appconsentv3.ui.listener.OnPresentNoticeListener;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        AppConsentSDK.initialize(
                "YOUR_APP_KEY",
                appConsentInitialized -> {

                    // Set a callback to let you know when the user has completed validation
                    registerCallback(appConsentInitialized);

                    // Once loaded, try to display CMP if needed
                    if (false == appConsentInitialized.tryToDisplayNoticeFromUiContext(this, false)) {
                        removeCMPCallback(appConsentInitialized);
                    }

                    return Unit.INSTANCE;
                }
        );
    }

    private void registerCallback(@NonNull final AppConsent appConsent) {
        appConsent.setOnPresentNoticeListener(new OnPresentNoticeListener() {
            @Override
            public void presentConsentError(@NonNull Throwable error) {
                // remove listener
                removeCMPCallback(appConsent);
                    
                // Load Ads providers
                ...
            }

            @Override
            public void presentConsentGiven() {
                // remove listener
                removeCMPCallback(appConsent);

                // Load Ads providers
                ...
            }
        });
    }

    private void removeCMPCallback(@NonNull final AppConsent appConsent) {
        appConsent.setOnPresentNoticeListener(null);
    }
}
```

{% endtab %}

{% tab title="TV" %}
{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.\
The potential **imports** and **visual elements** in these examples are therefore not present, as they are strongly linked to the Android structure and project, which are independent of the SDK.
{% endhint %}

**Kotlin**

```kotlin
import com.sfbx.appconsent.AppConsent
import com.sfbx.appconsent.tv.AppConsentSDK
import com.sfbx.appconsent.tv.listener.OnPresentNoticeListener
import com.sfbx.appconsent.tv.model.error.ACError

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        AppConsentSDK.initialize(
            appKey = "YOUR_APP_KEY"
        ) { appConsentInitialized ->
            
            // Set a callback to let you know when the user has completed validation
            registerCallback(appConsentInitialized)
            
            // Once loaded, try to display CMP if needed
            if(false == appConsentInitialized.tryToDisplayNotice(false)) {
                appConsentInitialized.setOnPresentNoticeListener(null)
            }
        }
    }

    private fun registerCallback(appConsent: AppConsent){
        appConsent.setOnPresentNoticeListener(object : OnPresentNoticeListener {

             override fun presentConsentError(error: ACError) {
                // remove listener
                appConsent.setOnPresentNoticeListener(null)
                    
                // Load Ads providers
                ...
            }

            override fun presentConsentGiven() {
                // remove listener
                appConsent.setOnPresentNoticeListener(null)

                // Load Ads providers
                ...
            }
        })
    }
}
```

**Java**

```java
import com.sfbx.appconsent.AppConsent;
import com.sfbx.appconsent.tv.AppConsentSDK;
import com.sfbx.appconsent.tv.listener.OnPresentNoticeListener;
import com.sfbx.appconsent.tv.model.error.ACError;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        AppConsentSDK.initialize(
            "YOUR_APP_KEY",
            appConsentInitialized -> {

                // Set a callback to let you know when the user has completed validation
                registerCallback(appConsentInitialized);
            
                // Once loaded, try to display CMP if needed
                if(false == appConsentInitialized.tryToDisplayNotice(false)) {
                    removeCMPCallback(appConsentInitialized);
                }

                return Unit.INSTANCE;
            }
        );
    }

    private void registerCallback(@NonNull final AppConsent appConsent) {
        appConsent.setOnPresentNoticeListener(new OnPresentNoticeListener() {
            @Override
            public void presentConsentError(@NonNull ACError acError) {
                // remove listener
                removeCMPCallback(appConsent);
                    
                // Load Ads providers
                ...
            }

            @Override
            public void presentConsentGiven() {
                // remove listener
                removeCMPCallback(appConsent);

                // Load Ads providers
                ...
            }
        });
    }

    private void removeCMPCallback(@NonNull final AppConsent appConsent) {
        appConsent.setOnPresentNoticeListener(null);
    }
}
```

{% endtab %}
{% endtabs %}

### 3. Suggest CMP to your users

It is also advisable to provide your users with an entry allowing them to view and modify their consent, such as a configuration screen.

{% tabs %}
{% tab title="Mobile / Tablet" %}
{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.\
The potential **imports** and **visual elements** in these examples are therefore not present, as they are strongly linked to the Android structure and project, which are independent of the SDK.
{% endhint %}

**Kotlin**

```kotlin
import com.sfbx.appconsentv3.ui.AppConsentSDK
import com.sfbx.appconsentv3.ui.listener.OnPresentNoticeListener

class SecondActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding.buttonDisplayPrivacyPolicy.setOnClickListener {
            /*
             Registers with CMP callback to know when the user
             has updated consent, or if an error has occurred
             */
            AppConsentSDK.getInstance()
                ?.setOnPresentNoticeListener(object : OnPresentNoticeListener {
                    override fun presentConsentError(error: Throwable?) {
                        // remove listener
                        removeCMPCallback()
                    }

                    override fun presentConsentGiven() {
                        // remove listener
                        removeCMPCallback()
                        
                        // Refresh Ads provider
                    }
                })

            // Try to display the CMP to allow your users to consult their consent
            val isCmpDisplayed =
                AppConsentSDK.getInstance()?.tryToDisplayNoticeFromUiContext(uiContext = this@SecondActivity, force = true) ?: false
            if (false == isCmpDisplayed) {
                /*
                The CMP is not initialized (getInstance() return null),
                make sure it is initialized (see 2. Create the AppConsent instance).
                Also check that your activity has not been started in a new process
                (new process, new memory stack, uninitialized singleton).
                 */
                removeCMPCallback()
            }
        }
    }

    private fun removeCMPCallback() {
        /*
        Not mandatory, but avoids keeping a local reference to the callback.
        Of course, it all depends on how your project is implemented.
        For example, with an SOP Activity, a singleton monitored by a flow, etc.
         */
        AppConsentSDK.getInstance()?.setOnPresentNoticeListener(null)
    }
}
```

**Java**

```java
import com.sfbx.appconsentv3.AppConsent;
import com.sfbx.appconsentv3.ui.AppConsentSDK;
import com.sfbx.appconsentv3.ui.listener.OnPresentNoticeListener;

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        findViewById(R.id.button_display_privacy_policy).setOnClickListener(v -> {
            final AppConsent appConsent = AppConsentSDK.getInstance();

            /*
            Display the CMP to allow your users to consult their consent
            */
            boolean isCmpDisplayed = false;
            if (appConsent != null) {
                defineCallback(appConsent);
                isCmpDisplayed = appConsent.tryToDisplayNoticeFromUiContext(this, true);
            }

            if (false == isCmpDisplayed) {
                /*
                The CMP is not initialized (getInstance() return null),
                make sure it is initialized (see 2. Create the AppConsent instance).
                Also check that your activity has not been started in a new process
                (new process, new memory stack, uninitialized singleton).
                */
                if (appConsent != null) {
                    removeCMPCallback(appConsent);
                }
            }
        });
    }

    private void defineCallback(@NonNull final AppConsent appconsent) {
        Objects.requireNonNull(appconsent).setOnPresentNoticeListener(new OnPresentNoticeListener() {
            @Override
            public void presentConsentError(@Nullable Throwable throwable) {
                /*
                An error has occurred
                */
                removeCMPCallback(appconsent);
            }

            @Override
            public void presentConsentGiven() {
                /*
                The user has updated his consent
                */
                removeCMPCallback(appconsent);

                // Refresh Ads provider
            }
        });
    }

    private void removeCMPCallback(@NonNull final AppConsent appconsent) {
        appconsent.setOnPresentNoticeListener(null);
    }
}
```

{% endtab %}

{% tab title="TV" %}
{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.\
The potential **imports** and **visual elements** in these examples are therefore not present, as they are strongly linked to the Android structure and project, which are independent of the SDK.
{% endhint %}

**Kotlin**

```kotlin
import com.sfbx.appconsent.tv.AppConsentSDK
import com.sfbx.appconsent.tv.listener.OnPresentNoticeListener
import com.sfbx.appconsent.tv.model.error.ACError

class SecondActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        findViewById<View>(R.id.button_display_privacy_policy).setOnClickListener {
            /*
             Registers with CMP callback to know when the user
             has updated consent, or if an error has occurred
             */
            AppConsentSDK.getInstance()
                ?.setOnPresentNoticeListener(object : OnPresentNoticeListener {
                    override fun presentConsentError(error: ACError) {
                        /*
                         An error has occurred
                         */
                        removeCMPCallback()
                    }

                    override fun presentConsentGiven() {
                        /*
                        The user has updated his consent
                        */
                        removeCMPCallback()

                        // Refresh Ads provider
                    }
                })
            /*
            Display the CMP to allow your users to consult their consent
            */
            val isCmpDisplayed =
                AppConsentSDK.getInstance()?.tryToDisplayNotice(force = true) ?: false
            if (false == isCmpDisplayed) {
                /*
                The CMP is not initialized (getInstance() return null),
                make sure it is initialized (see 2. Create the AppConsent instance).
                Also check that your activity has not been started in a new process
                (new process, new memory stack, uninitialized singleton).
                 */
                removeCMPCallback()
            }
        }
    }

    private fun removeCMPCallback() {
        /*
        Not mandatory, but avoids keeping a local reference to the callback.
        Of course, it all depends on how your project is implemented.
        For example, with an SOP Activity, a singleton monitored by a flow, etc.
         */
        AppConsentSDK.getInstance()?.setOnPresentNoticeListener(null)
    }
}
```

**Java**

```java
import com.sfbx.appconsent.AppConsent;
import com.sfbx.appconsent.tv.AppConsentSDK;
import com.sfbx.appconsent.tv.listener.OnPresentNoticeListener;
import com.sfbx.appconsent.tv.model.error.ACError;

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        findViewById(R.id.button_display_privacy_policy).setOnClickListener(v -> {
            final AppConsent appConsent = AppConsentSDK.getInstance();

            /*
            Display the CMP to allow your users to consult their consent
            */
            boolean isCmpDisplayed = false;
            if (appConsent != null) {
                defineCallback(appConsent);
                isCmpDisplayed = appConsent.tryToDisplayNotice(true);
            }

            if (false == isCmpDisplayed) {
                /*
                The CMP is not initialized (getInstance() return null),
                make sure it is initialized (see 2. Create the AppConsent instance).
                Also check that your activity has not been started in a new process
                (new process, new memory stack, uninitialized singleton).
                */
                if (appConsent != null) {
                    removeCMPCallback(appConsent);
                }
            }
        });
    }

    private void defineCallback(@NonNull final AppConsent appconsent) {
        Objects.requireNonNull(appconsent).setOnPresentNoticeListener(new OnPresentNoticeListener() {
            @Override
            public void presentConsentError(@NonNull ACError acError) {
                /*
                An error has occurred
                */
                removeCMPCallback(appconsent);
            }

            @Override
            public void presentConsentGiven() {
                /*
                The user has updated his consent
                */
                removeCMPCallback(appconsent);

                // Refresh Ads provider
            }
        });
    }

    private void removeCMPCallback(@NonNull final AppConsent appconsent) {
        appconsent.setOnPresentNoticeListener(null);
    }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**NOTE**

Would you like a more complete example?\
We suggest you read the [**Go further**](/configuration/step-3-notice-implementation-web-app-tv/android/old-sdk-appconsentsdk/go-further) page.
{% endhint %}


# How to use it?

***

## How to use appConsent object?

Here is a list of the most commonly used methods

### **Check if user gave consent**

```kotlin
appConsent.consentGiven()
```

Return `true` if consent is given, `false` otherwise.<br>

{% hint style="danger" %}
**WARNING**

Please note that we are only talking about whether the user has confirmed a **choice** and not whether he has **accepted** or **refused** consent.
{% endhint %}

### **Define the listener related to user consent**

When the user has completed the consent process and given their consent, this "callback" informs them of this.

{% hint style="info" %}
**INFO**

It is recommended to define it before trying to display the CMP and to remove it once consent has been given or the cmp has not been displayed.
{% endhint %}

```kotlin
appConsent.setOnPresentNoticeListener(object : OnPresentNoticeListener { 
    override fun presentConsentGiven() {
        // ...
    }

    override fun presentConsentError(error: Throwable?) {
        // ...
    }
})
```

### **Remove Listener**

```kotlin
appConsent.setOnPresentNoticeListener(null)
```

### **Try to Display CMP notice**

There are 2 display modes:

1. The first runs only if user consent is required.
2. The second, which should be used to allow your users to consult/modify their consent *(often used from your settings screen to display your user's privacy policy)*

{% hint style="info" %}
**INFO**

By default this method tries to display the CMP. It tries because, depending on the region of your users (if no settings are made via ACConfiguration to force the display) then it will follow its controls and display the CMP only if necessary.

It will also be displayed if the user consent has not yet been given or if it needs to be renewed.
{% endhint %}

```kotlin
// @return true if the notice display, false otherwise
appConsent.tryToDisplayNoticeFromUiContext(uiContext, false) // display CMP notice only if needed
appConsent.tryToDisplayNoticeFromUiContext(uiContext, true) // force to display CMP notice
```

It returns `true` if the CMP is displayed, `false` otherwise

{% hint style="info" %}
**INFO**

These same methods also exist without the context parameter.

\
These methods do the same thing, except that they implement the `FLAG_ACTIVITY_NEW_TASK` flag to be called from a context other than an activity.

```kotlin
// @return true if the notice display, false otherwise
appConsent.tryToDisplayNotice(false) // display CMP notice only if needed
appConsent.tryToDisplayNotice(true) // force to display CMP notice
```

{% endhint %}

### **GCM Status**

{% hint style="info" %}
**INFO**

This method shows the **current** status of GCMv2 (Google Consent Mode V2).

Before calling up this method, it's best to make sure that the user has **already given his consent** and that it's up to date, and that the CMP doesn't need to be redisplayed.

Otherwise :

* either the saved value of the **old consent** will be returned
* or the **default values** of your FirebaseAnalytics AndroidManifest configuration will be returned

[Set the default consent state from google documentation](https://developers.google.com/tag-platform/security/guides/app-consent?consentmode=advanced\&platform=android#default-consent)
{% endhint %}

```kotlin
appConsent.getGCMConsentStatus()
```

Return `GCMStatus`

<details>

<summary>Data model representing the state of GCMv2 (Google Consent Mode v2)</summary>

This will allow you to define consent from your Firebase Analytics instance.

`isAnalyticsStorageGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#ANALYTICS\_STORAGE**

`isAdStorageGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#AD\_STORAGE**

`isAdUserDataGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#AD\_USER\_DATA**

`isAdPersonalizationGranted`, indicates whether the user has given consent for **FirebaseAnalytics.ConsentType#AD\_PERSONALIZATION**

[FirebaseAnalytics.ConsentType](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.ConsentType)

</details>

### **Check for update**

This method allows you to check from our servers whether your Notice has been updated since it was last displayed on your user's device.

{% hint style="info" %}
**INFO**

The method will return true if you have modified the Source and/or Notice from your dashboard and, if and **only if**, you have configured your Notice to update for all your users.
{% endhint %}

<figure><img src="/files/9qusg58AojkqxJkrT4ZL" alt=""><figcaption></figcaption></figure>

{% hint style="danger" %}
**ATTENTION**

This method can only be used once every **30 minutes**. This allows you to force a first network call to make sure your users are up to date. The next calls to this method will use the cache of the previous response or attempt a network call if the previous one was in error.
{% endhint %}

```kotlin
appConsent.checkForUpdate({ isNeedToPresentTheCmp: Boolean ->
            // Your Notice has been updated, you must represent the CMP to your users
            if (true == isNeedToPresentTheCmp) {
                /*
                Deletes old user consent locally.
                This step is not mandatory, but it avoids the need to make another network call
                to check whether the Notice has been updated,
                as no consent will be present on the user's device.
                 */
                appConsent.clearConsent()
                appConsent.tryToDisplayNotice(false)
            } else {
                /*
                The Notice is the same as when it was last checked
                (you have made no changes since the board or
                it has not been updated internally by us, e.g. by updating a vendor)
                 */
            }
        }) { _: Throwable? ->
            /*
             An error has occurred
             */
        }
```

## Using AppConsent's more specific methods

Here's a list of methods that could be useful if you want to go further in tracking user consent.

### **Consentable allowed**

```kotlin
appConsent.consentableAllowed(1,0)
```

Return `true` if consentable with `id = 1` and `consentableType = 0` is allowed, `false` otherwise. The `id` to pass is the `iabId` of your purpose and the `consentableType` is the type, e.g: `purpose = 0` .

### **Stack Allowed**

```kotlin
appConsent.stackAllowed(1)
```

Return `true` if stack with `id = 1` is allowed, `false` otherwise.

### **Vendor allowed**

```kotlin
appConsent.vendorAllowed(1)
```

Return `true` if vendor with `id = 1` is allowed, `false` otherwise.

### **All Consentables Allowed**

```kotlin
appConsent.isAllConsentablesAllowed()
```

Returns `true` if all consentables have been allowed `false` if at least one consentable is not allowed and `null` if no choice has yet been made (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **All Consentables Disallowed**

```kotlin
appConsent.isAllConsentablesDisallowed()
```

Returns `true` if all consentables have been disallowed `false` if at least one consentable is not disallowed and `null` if no choice has yet been made (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **All Stacks Allowed**

```kotlin
appConsent.isAllStacksAllowed()
```

Returns `true` if all stacks have been accepted `false` if at least one stack is not accepted and `null` if no choice has yet been made or not present into your notice (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **All Stacks Disallowed**

```kotlin
appConsent.isAllStacksDisallowed()
```

Returns `true` if all stacks have been disallowed `false` if at least one stack is not disallowed and `null` if no choice has yet been made or not present into your notice (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **All Vendors Allowed**

```kotlin
appConsent.isAllVendorsAllowed()
```

Returns `true` if all vendors have been allowed `false` if at least one vendor is not allowed and `null` if no choice has yet been made (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **All Vendors Disallowed**

```kotlin
appConsent.isAllVendorsDisallowed()
```

Returns `true` if all vendors have been disallowed `false` if at least one vendor is not disallowed and `null` if no choice has yet been made (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **User Accept All**

```kotlin
appConsent.isUserAcceptAll()
```

Returns `true` if all consent items, stacks and vendors are allowed. `false` if at least one of them is not allowed and `null` if no data is present yet (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **User Deny All**

```kotlin
appConsent.isUserDenyAll()
```

Returns `true` if all consent items, stacks and vendors are disallowed. `false` if at least one of them is not disallowed and `null` if no data is present yet (notice not yet downloaded, choice not yet made, application cache deleted, etc.).

### **Set consentable status**

```kotlin
appConsent.setConsentableConsents(
    mapOf(1 to ConsentStatus.ALLOWED, 2 to ConsentStatus.DISALLOWED),
    object : AppConsentSetConsentableConsentsCallback {
        override fun onSuccess() {
            // ...
        }

        override fun onError(t: Throwable) {
            // ...
        }
    }
)
```

Set consentables status, save it and send it to server.

### **Clear consents**

Locally removes user consent, but not on the server (this will allow a new display of the CMP on the next call to `tryToDisplayNotice(false)` for example)

```kotlin
appConsent.clearConsent()
```

### **Set external ids**

```kotlin
val ids = mapOf<String, String>("customPersonalId" to "abze23", "otherData" to "{\"name\": \"test\"}")
appConsent.setExternalIds(ids)
```

Allows to define additional Ids that will be taken into account when validating user consent.

### **Get external ids**

Retrieves your previously registered external ids

```kotlin
val ids: Map<String, String> = appConsent.getExternalIds()
```

## (Bonus) Retrieve your consents

Your consents are saved in `SharedPreferences` of your application. To know more about keys used to save your consents, please refer to the [**IAB documentation**](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details).

We also provide an additional key for Google Additionnal Consent `IABTCF_AddtlConsent` returning a String.


# Go further

Examples

***

{% content-ref url="/pages/bJPxQ4JAEtn8KsOZbfYX" %}
[Mobile / Tablet](/configuration/step-3-notice-implementation-web-app-tv/android/old-sdk-appconsentsdk/go-further/mobile-tablet)
{% endcontent-ref %}

{% content-ref url="/pages/VRh5t23aYaenzGnwhKwn" %}
[TV](/configuration/step-3-notice-implementation-web-app-tv/android/old-sdk-appconsentsdk/go-further/tv)
{% endcontent-ref %}


# Mobile / Tablet

<details>

<summary>Full description of the integration example below</summary>

In this example, we'll make sure that the CMP is displayed to users when the application is launched (startup of our activity).

**Here are the various steps in this example at first launch**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-at-first-launch)

1. Check SDK if already initialized (return false)
2. Initialize the CMP by calling **AppConsentSDK#initialize** if not.
3. Once the SDK has been started, record callbacks to let you know when the user has finished entering information.
4. Call the **#tryToDisplayNoticeFromUiContext(uiContext, false)** method, which displays the CMP only when necessary. (in the example, it will be displayed).
5. check the return of **#tryToDisplayNoticeFromUiContext(uiContext, false)** to determine whether the CMP has been displayed or not (return to true).
6. Once the user has given their consent, the **#presentConsentGiven** method will be called and you can, for example, start up your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.).

**Here are the various steps in this example on the next launch (having killed the application)**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-on-the-next-launch-having-killed-the-application)

1. Check SDK if it has already been initialized (return false)
2. Initialize the CMP by calling **AppConsentSDK#initialize**, if not.
3. Once the SDK has been started, record callbacks to let you know when the user has finished entering information.
4. Call the **#tryToDisplayNoticeFromUiContext(uiContext, false)** method, which displays the CMP only if necessary.
5. check the return of **#tryToDisplayNoticeFromUiContext(uiContext, false)** to determine whether or not the CMP has been displayed (result set to false).
6. **(By your own)** Try to call the **#checkForUpdate** method to check whether the Notice has been updated from your Dashboard (unless you take action, result is false).
7. No action is required, so you can start up your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.).

**Here are the various steps in this example on the next launch (quitting the application and bringing it forward again without killing it)**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-on-the-next-launch-quitting-the-application-and-bringing-it-forward-again-without-killing-it)

1. Check SDK if it has already been initialized (return to true)
2. Recover instance for local use in our activity
3. Call the **#tryToDisplayNoticeFromUiContext(uiContext, false)** method, which displays the CMP only if necessary.
4. check the return of **#tryToDisplayNoticeFromUiContext(uiContext, false)** to determine whether the CMP has been displayed or not (result set to false).
5. **(By your own)** Try to call the **#checkForUpdate** method to check whether the Notice has been updated from your Dashboard (unless you take action, result is false).
6. No action is required, so you can start up your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.).

**Here are the various steps in this example at the next launch AND after you've modified your Notice from the Dashboard (by quitting the application and bringing it forward again without killing it)**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-at-the-next-launch-and-after-youve-modified-your-notice-from-the-dashboard-by-quitting-the-application-and-bringing-it-forward-again-without-killing-it)

1. Check SDK if it has already been initialized (return to true)
2. Recover instance for local use in our activity
3. Call the **#tryToDisplayNoticeFromUiContext(uiContext, false)** method, which displays the CMP only if necessary.
4. check the return of **#tryToDisplayNoticeFromUiContext(uiContext, false)** to determine whether the CMP has been displayed or not (result set to false).
5. **(By your own)** Try to call the **#checkForUpdate** method to check whether the Notice has been updated from your Dashboard (result is true because your Notice has been updated).
6. Local deletion of previous user consent **#clearConsent**
7. Call the **#tryToDisplayNoticeFromUiContext(uiContext, false)** method to display the CMP only if necessary. (return to true)
8. Once the user has given consent, the **#presentConsentGiven** method will be called and you can, for example, start your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.)

</details>

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import com.sfbx.appconsentv3.AppConsent
import com.sfbx.appconsentv3.ui.AppConsentSDK
import com.sfbx.appconsentv3.ui.listener.OnPresentNoticeListener
import com.sfbx.appconsentv3.ui.model.ACConfiguration

class MainActivity : AppCompatActivity() {

    private var appConsent: AppConsent? = null
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)

        setContentView(binding.root)

        /*
        Shows whether the SDK has already been initialized
         */
        if (!AppConsentSDK.isSdkInitialized() || AppConsentSDK.getInstance() == null) {
            initCmpModule()
        } else {
            if (appConsent == null) {
                /*
                Retrieves the AppConsent instance or null if it has not yet been instantiated, for example
                 */
                appConsent = AppConsentSDK.getInstance()
            }
            tryToDisplayCmpAndCheckUpdateIfNeeded()
        }
    }

    /**
     * The only conditions for using this method would be if:
     * - you have configured your Notice by selecting this option: When saving changes to the notice, display the notice to all visitors.
     * - you plan to update your Notice often (more often than the : Consent retention period configurable in your Notice)
     * - Your users rarely restart your application.
     *
     * In which case, using this method from time to time may be a solution.
     *
     * But we encourage you to let the SDK handle this part on its own.
     */
    private fun checkIfNoticeHasBeenUpdated() {
        appConsent?.checkForUpdate(
            { isNeedToPresentTheCmp ->
                // Your Notice has been updated, you must represent the CMP to your users
                if (true == isNeedToPresentTheCmp) {
                    /*
                    Deletes old user consent locally.
                    This step is not mandatory, but it avoids the need to make another network call
                    to check whether the Notice has been updated,
                    as no consent will be present on the user's device.
                     */
                    appConsent?.clearConsent()

                    /*
                    Remember to re-register for callbacks if you have previously removed them,
                    otherwise you will not know when the user has given their consent.
                     */
                    registerCallbacks()
                    if (false == tryToDisplayCMP()) {
                        /*
                         At this stage, if the CMP is not displayed, it is possible, for example:
                         that the user is no longer in a geographical area subject to GDPR
                         */
                        removeCMPCallback()
                    }
                } else {
                    /*
                    The Notice is the same as when it was last checked
                    (you have made no changes since the board or
                    it has not been updated internally by us, e.g. by updating a vendor)
                     */
                }
            },
            { _ ->
                /*
                 An error has occurred
                 */
            })
    }

    /**
     * Try to display the CMP
     *
     * @return true, if the CMP displaying, false otherwise
     */
    private fun tryToDisplayCMP(): Boolean {
        /*
         Try to display the CMP according to certain rules.
         */
        return appConsent?.tryToDisplayNoticeFromUiContext(uiContext = this@MainActivity, false) == true
    }

    private fun tryToDisplayCmpAndCheckUpdateIfNeeded() {
        /*
         Try to display the CMP according to certain rules first.
         */
        if (false == tryToDisplayCMP()) {
            /*
             The user has already given consent;
             The user is not part of an area subject to the application of GDPR;
             etc.
             */
            removeCMPCallback()

            /*
            The Notice has not been displayed,
            so we'll have to check whether it has been updated
            and whether it needs to be shown to users again.
             */
            checkIfNoticeHasBeenUpdated()
        }
    }

    private fun registerCallbacks() {
        /*
         Registers with CMP callback to know when the user
         has given consent, or if an error has occurred
         */
        appConsent?.setOnPresentNoticeListener(object : OnPresentNoticeListener {
            override fun presentConsentError(error: Throwable?) {
                /*
                 An error has occurred
                 */
                removeCMPCallback()
            }

            override fun presentConsentGiven() {
                /*
                The user has given his consent
                */
                appConsent?.let { appConsentNN ->
                    updateFirebaseConsent(appConsentNN)
                }
                removeCMPCallback()
            }
        })
    }

    /*
     Initializes the consent management platform module when the activity is created
     */
    private fun initCmpModule() {
        /*
        ACConfiguration is used to configure the CMP.
        In this example:
        - We set forceApplyGDPR to true to display the CMP regardless of the user's region.
        - Decide to display the CMP in FullScreen rather than modal.
        - CMP is configured so that layer 1 buttons are displayed vertically and not horizontally (except in landscape mode).
        - We configure CMP so that hypertext links are no longer displayed in a webview and/or the user is redirected outside the application if the requested link is a file, for example; instead, a popup presenting a qr code will be presented to your users (mostly useful on Automotive / Tablet)
         */
        val acConfiguration = ACConfiguration.Builder()
            .setForceApplyGDPR(true)
            .setFullScreenMode(true)
            .setNeedToDisplayValidationButtonsVertically(isNeedToDisplayButtonsAtVertical = true)
            .setNeedToReplaceUrlViewerByQrCode(isNeedToReplaceUrlViewerByQrCode = true)
            .build()

        AppConsentSDK.initialize(
            appKey = "YOUR_APPKEY",
            configuration = acConfiguration
        ) { appConsentInitialized ->
            /*
             To avoid certain problems, use the instance received by the onReady callback
             This has been successfully initialized
             */
            appConsent = appConsentInitialized

            registerCallbacks()
            tryToDisplayCmpAndCheckUpdateIfNeeded()
        }
    }

    private fun removeCMPCallback() {
        /*
        Not mandatory, but avoids keeping a local reference to the callback.
        Of course, it all depends on how your project is implemented.
        For example, with an SOP Activity, a singleton monitored by a flow, etc.
         */
        appConsent?.setOnPresentNoticeListener(null)
    }

    private fun updateFirebaseConsent(appConsent: AppConsent) {
        // Consent has just been validated by the user.
        // Recovers current GCM status (following user consent).
        val gcmConsentStatus = appConsent.getGCMConsentStatus()

        val newAnalyticsStorage =
            if (gcmConsentStatus.isAnalyticsStorageGranted) GRANTED else DENIED
        val newAdStorage =
            if (gcmConsentStatus.isAdStorageGranted) GRANTED else DENIED
        val newAdUserData =
            if (gcmConsentStatus.isAdUserDataGranted) GRANTED else DENIED
        val newAdPersonalization =
            if (gcmConsentStatus.isAdPersonalizationGranted) GRANTED else DENIED

        /*
        * We do not guarantee that Firebase will work with this example.
        * Please refer to the official documentation for details of initialization, re-initialization, reboot and other conditions.
        *
        * This example is only intended to show how to retrieve ConsentStatus and set it to your Firebase instance.
        */
        // Update Firebase status with information provided by the SDK
        Firebase.analytics.setConsent {
            analyticsStorage = newAnalyticsStorage
            adStorage = newAdStorage
            adUserData = newAdUserData
            adPersonalization = newAdPersonalization
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
import com.sfbx.appconsent.core.model.gcm.GCMStatus;
import com.sfbx.appconsentv3.AppConsent;
import com.sfbx.appconsentv3.ui.AppConsentSDK;
import com.sfbx.appconsentv3.ui.AppConsentTheme;
import com.sfbx.appconsentv3.ui.listener.OnPresentNoticeListener;
import com.sfbx.appconsentv3.ui.model.ACConfiguration;

public class MainActivity extends AppCompatActivity {
    private AppConsent appConsent = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /*
        Shows whether the SDK has already been initialized
         */
        if (!AppConsentSDK.isSdkInitialized() || AppConsentSDK.getInstance() == null) {
            initCmpModule();
        } else {
            if (appConsent == null) {
                /*
                Retrieves the AppConsent instance or null if it has not yet been instantiated, for example
                 */
                appConsent = AppConsentSDK.getInstance();
            }
            tryToDisplayCmpAndCheckUpdateIfNeeded();
        }
    }

    /**
     * The only conditions for using this method would be if:
     * - you have configured your Notice by selecting this option: When saving changes to the notice, display the notice to all visitors.
     * - you plan to update your Notice often (more often than the : Consent retention period configurable in your Notice)
     * - Your users rarely restart your application.
     *
     * In which case, using this method from time to time may be a solution.
     *
     * But we encourage you to let the SDK handle this part on its own.
     */
    private void checkIfNoticeHasBeenUpdated() {
        appConsent.checkForUpdate(
                isNeedToPresentTheCmp -> {
                    // Your Notice has been updated, you must represent the CMP to your users
                    if (true == isNeedToPresentTheCmp) {
                    /*
                    Deletes old user consent locally.
                    This step is not mandatory, but it avoids the need to make another network call
                    to check whether the Notice has been updated,
                    as no consent will be present on the user's device.
                 */
                        appConsent.clearConsent();

                /*
                Remember to re-register for callbacks if you have previously removed them,
                otherwise you will not know when the user has given their consent.
                 */
                        registerCallbacks();
                        if (false == tryToDisplayCMP()) {
                        /*
                         At this stage, if the CMP is not displayed, it is possible, for example:
                         that the user is no longer in a geographical area subject to GDPR
                         */
                            removeCMPCallback();
                        }
                        return Unit.INSTANCE;
                    } else {
                /*
                The Notice is the same as when it was last checked
                (you have made no changes since the board or
                it has not been updated internally by us, e.g. by updating a vendor)
                 */
                    }
                    return Unit.INSTANCE;
                }, onError -> {
            /*
             An error has occurred
             */
                    return Unit.INSTANCE;
                });
    }

    /**
     * Try to display the CMP
     *
     * @return true, if the CMP displaying, false otherwise
     */
    private Boolean tryToDisplayCMP() {
        /*
         Try to display the CMP according to certain rules.
         */
        return (appConsent.tryToDisplayNoticeFromUiContext(this, false) == true);
    }

    private void tryToDisplayCmpAndCheckUpdateIfNeeded() {
        /*
         Try to display the CMP according to certain rules first.
         */
        if (false == tryToDisplayCMP()) {
            /*
             The user has already given consent;
             The user is not part of an area subject to the application of GDPR;
             etc.
             */
            removeCMPCallback();

            /*
            The Notice has not been displayed,
            so we'll have to check whether it has been updated
            and whether it needs to be shown to users again.
             */
            checkIfNoticeHasBeenUpdated();
        }
    }

    private void registerCallbacks() {
        /*
         Registers with CMP callback to know when the user
         has given consent, or if an error has occurred
         */
        appConsent.setOnPresentNoticeListener(new OnPresentNoticeListener() {
            @Override
            public void presentConsentError(@Nullable Throwable throwable) {
                /*
                 An error has occurred
                 */
                removeCMPCallback();
            }

            @Override
            public void presentConsentGiven() {
                /*
                The user has given his consent
                */
                if (appConsent != null) {
                    updateFirebaseConsent(appConsent);
                }
                removeCMPCallback();
            }
        });
    }

    /*
     Initializes the consent management platform module when the activity is created
     */
    private void initCmpModule() {
        /*
        ACConfiguration is used to configure the CMP.
        In this example:
        - We set forceApplyGDPR to true to display the CMP regardless of the user's region.
        - Decide to display the CMP in FullScreen rather than modal.
        - CMP is configured so that layer 1 buttons are displayed vertically and not horizontally (except in landscape mode).
        - We configure CMP so that hypertext links are no longer displayed in a webview and/or the user is redirected outside the application if the requested link is a file, for example; instead, a popup presenting a qr code will be presented to your users (mostly useful on Automotive / Tablet)
         */
        final ACConfiguration acConfiguration = new ACConfiguration.Builder()
                .setForceApplyGDPR(true)
                .setFullScreenMode(true)
                .setNeedToDisplayValidationButtonsVertically(true)
                .setNeedToReplaceUrlViewerByQrCode(true)
                .defineAppConsentTheme(new AppConsentTheme.Builder(this).iconDrawable(null).build())
                .build();

        AppConsentSDK.initialize(
                "YOUR_APPKEY",
                acConfiguration,
                appConsentInitialized -> {
                    /*
                     To avoid certain problems, use the instance received by the onReady callback
                     This has been successfully initialized
                     */
                    appConsent = appConsentInitialized;

                    registerCallbacks();
                    tryToDisplayCmpAndCheckUpdateIfNeeded();
                    return Unit.INSTANCE;
                });
    }

    private void removeCMPCallback() {
        /*
        Not mandatory, but avoids keeping a local reference to the callback.
        Of course, it all depends on how your project is implemented.
        For example, with an SOP Activity, a singleton monitored by a flow, etc.
         */
        if (appConsent != null) {
            appConsent.setOnPresentNoticeListener(null);
        }
    }

    private void updateFirebaseConsent(AppConsent appConsent) {
        // Consent has just been validated by the user.
        // Recovers current GCM status (following user consent).
        final GCMStatus gcmConsentStatus = appConsent.getGCMConsentStatus();

        final FirebaseAnalytics.ConsentStatus newAnalyticsStorage;
        if (gcmConsentStatus.isAnalyticsStorageGranted()) newAnalyticsStorage = GRANTED;
        else newAnalyticsStorage = DENIED;
        final FirebaseAnalytics.ConsentStatus newAdStorage;
        if (gcmConsentStatus.isAdStorageGranted()) newAdStorage = GRANTED;
        else newAdStorage = DENIED;
        final FirebaseAnalytics.ConsentStatus newAdUserData;
        if (gcmConsentStatus.isAdUserDataGranted()) newAdUserData = GRANTED;
        else newAdUserData = DENIED;
        final FirebaseAnalytics.ConsentStatus newAdPersonalization;
        if (gcmConsentStatus.isAdPersonalizationGranted()) newAdPersonalization = GRANTED;
        else newAdPersonalization = DENIED;

        /*
         * We do not guarantee that Firebase will work with this example.
         * Please refer to the official documentation for details of initialization, re-initialization, reboot and other conditions.
         *
         * This example is only intended to show how to retrieve ConsentStatus and set it to your Firebase instance.
         */
        // Update Firebase status with information provided by the SDK
        final Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentStatus = new HashMap<>();
        consentStatus.put(FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE, newAnalyticsStorage);
        consentStatus.put(FirebaseAnalytics.ConsentType.AD_STORAGE, newAdStorage);
        consentStatus.put(FirebaseAnalytics.ConsentType.AD_USER_DATA, newAdUserData);
        consentStatus.put(FirebaseAnalytics.ConsentType.AD_PERSONALIZATION, newAdPersonalization);
        FirebaseAnalytics.getInstance(getApplicationContext()).setConsent(consentStatus);
    }
}
```

{% endtab %}
{% endtabs %}


# TV

<details>

<summary>Full description of the integration example below</summary>

In this example, we'll make sure that the CMP is displayed to users when the application is launched (startup of our activity).

**Here are the various steps in this example at first launch**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-at-first-launch)

1. Check SDK if already initialized (return false)
2. Initialize the CMP by calling **AppConsentSDK#initialize** if not.
3. Once the SDK has been started, record callbacks to let you know when the user has finished entering information.
4. Call the **#tryToDisplayNotice(false)** method, which displays the CMP only when necessary. (in the example, it will be displayed).
5. check the return of **#tryToDisplayNotice(false)** to determine whether the CMP has been displayed or not (return to true).
6. Once the user has given their consent, the **#presentConsentGiven** method will be called and you can, for example, start up your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.).

**Here are the various steps in this example on the next launch (having killed the application)**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-on-the-next-launch-having-killed-the-application)

1. Check SDK if it has already been initialized (return false)
2. Initialize the CMP by calling **AppConsentSDK#initialize**, if not.
3. Once the SDK has been started, record callbacks to let you know when the user has finished entering information.
4. Call the **#tryToDisplayNotice(false)** method, which displays the CMP only if necessary.
5. check the return of **#tryToDisplayNotice(false)** to determine whether or not the CMP has been displayed (result set to false).
6. **(By your own)** Try to call the **#checkForUpdate** method to check whether the Notice has been updated from your Dashboard (unless you take action, result is false).
7. No action is required, so you can start up your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.).

**Here are the various steps in this example on the next launch (quitting the application and bringing it forward again without killing it)**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-on-the-next-launch-quitting-the-application-and-bringing-it-forward-again-without-killing-it)

1. Check SDK if it has already been initialized (return to true)
2. Recover instance for local use in our activity
3. Call the **#tryToDisplayNotice(false)** method, which displays the CMP only if necessary.
4. check the return of **#tryToDisplayNotice(false)** to determine whether the CMP has been displayed or not (result set to false).
5. **(By your own)** Try to call the **#checkForUpdate** method to check whether the Notice has been updated from your Dashboard (unless you take action, result is false).
6. No action is required, so you can start up your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.).

**Here are the various steps in this example at the next launch AND after you've modified your Notice from the Dashboard (by quitting the application and bringing it forward again without killing it)**[**​**](https://docs.sfbx.io/configuration/notice-implementation/android/go_further#here-are-the-various-steps-in-this-example-at-the-next-launch-and-after-youve-modified-your-notice-from-the-dashboard-by-quitting-the-application-and-bringing-it-forward-again-without-killing-it)

1. Check SDK if it has already been initialized (return to true)
2. Recover instance for local use in our activity
3. Call the **#tryToDisplayNotice(false)** method, which displays the CMP only if necessary.
4. check the return of **#tryToDisplayNotice(false)** to determine whether the CMP has been displayed or not (result set to false).
5. **(By your own)** Try to call the **#checkForUpdate** method to check whether the Notice has been updated from your Dashboard (result is true because your Notice has been updated).
6. Local deletion of previous user consent **#clearConsent**
7. Call the **#tryToDisplayNotice(false)** method to display the CMP only if necessary. (return to true)
8. Once the user has given consent, the **#presentConsentGiven** method will be called and you can, for example, start your PUB SDK (AdMob, Vungle, Amazon mobile Ads, etc.)

</details>

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
import com.sfbx.appconsent.AppConsent
import com.sfbx.appconsent.tv.AppConsentSDK
import com.sfbx.appconsent.tv.listener.AppConsentErrorListener
import com.sfbx.appconsent.tv.listener.AppConsentLogListener
import com.sfbx.appconsent.tv.listener.AppConsentNavigationListener
import com.sfbx.appconsent.tv.listener.OnPresentNoticeListener
import com.sfbx.appconsent.tv.model.ACConfiguration
import com.sfbx.appconsent.tv.model.AppConsentTVError
import com.sfbx.appconsent.tv.model.AppConsentTVLog
import com.sfbx.appconsent.tv.model.error.ACError

class MainActivity : AppCompatActivity() {

    private var appConsent: AppConsent? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        /*
        Shows whether the SDK has already been initialized
         */
        if (!AppConsentSDK.isSdkInitialized() || AppConsentSDK.getInstance() == null) {
            initCmpModule()
        } else {
            if (appConsent == null) {
                /*
                Retrieves the AppConsent instance or null if it has not yet been instantiated, for example
                 */
                appConsent = AppConsentSDK.getInstance()
            }
            tryToDisplayCmpAndCheckUpdateIfNeeded()
        }
    }

    /**
     * The only conditions for using this method would be if:
     * - you have configured your Notice by selecting this option: When saving changes to the notice, display the notice to all visitors.
     * - you plan to update your Notice often (more often than the : Consent retention period configurable in your Notice)
     * - Your users rarely restart your application.
     *
     * In which case, using this method from time to time may be a solution.
     *
     * But we encourage you to let the SDK handle this part on its own.
     */
    private fun checkIfNoticeHasBeenUpdated() {
        appConsent?.checkForUpdate(
            { isNeedToPresentTheCmp: Boolean ->
                // Your Notice has been updated, you must represent the CMP to your users
                if (true == isNeedToPresentTheCmp) {
                    /*
                    Deletes old user consent locally.
                    This step is not mandatory, but it avoids the need to make another network call
                    to check whether the Notice has been updated,
                    as no consent will be present on the user's device.
                     */
                    appConsent?.clearConsent()

                    /*
                    Remember to re-register for callbacks if you have previously removed them,
                    otherwise you will not know when the user has given their consent.
                     */
                    registerCallbacks()
                    if (false == tryToDisplayCMP()) {
                        /*
                         At this stage, if the CMP is not displayed, it is possible, for example:
                         that the user is no longer in a geographical area subject to GDPR
                         */
                        removeCMPCallback()
                    }
                } else {
                    /*
                    The Notice is the same as when it was last checked
                    (you have made no changes since the board or
                    it has not been updated internally by us, e.g. by updating a vendor)
                     */
                    removeCMPCallback()
                }
            }) { _: Throwable? ->
            /*
             An error has occurred
             */
            removeCMPCallback()
        }
    }

    /**
     * Try to display the CMP
     *
     * @return true, if the CMP displaying, false otherwise
     */
    private fun tryToDisplayCMP(): Boolean {
        /*
         Try to display the CMP according to certain rules.
         */
        return appConsent?.tryToDisplayNotice(false) == true
    }

    private fun tryToDisplayCmpAndCheckUpdateIfNeeded() {
        /*
         Try to display the CMP according to certain rules first.
         */
        if (false == tryToDisplayCMP()) {
            /*
             The user has already given consent;
             The user is not part of an area subject to the application of GDPR;
             etc.
             */

            /*
            The Notice has not been displayed,
            so we'll have to check whether it has been updated
            and whether it needs to be shown to users again.
             */
            checkIfNoticeHasBeenUpdated()
        }
    }

    /*
     Initializes the consent management platform module when the activity is created
     */
    private fun initCmpModule() {
        /*
        Add an error listener logger
         */
        AppConsentSDK.setErrorLogListener(object : AppConsentErrorListener {
            override fun onError(error: AppConsentTVError) {
                Log.e(
                    MainActivity::class.java.simpleName,
                    error.toString()
                )
            }
        })

        /*
        Add an information listener logger
         */
        AppConsentSDK.setInformationLogListener(object : AppConsentLogListener {
            override fun onLogReceived(log: AppConsentTVLog) {
                Log.i(
                    MainActivity::class.java.simpleName,
                    log.toString()
                )
            }
        })

        /*
        Add a navigation listener logger (historical)
         */
        AppConsentSDK.setNavigationLogListener(object : AppConsentNavigationListener {
            override fun onNoticeBackPressed() {
                Log.i(
                    MainActivity::class.java.simpleName,
                    "onNoticeBackPressed"
                )
            }
        })

        /*
         ACConfiguration is used to configure the CMP.
         In this example:
         - We set forceApplyGDPR to true to display the CMP regardless of the user's region.
         - We set setNeedToReplaceUrlViewerByQrCode to true to display QR codes instead of the embedded webview for hyperlinks.
        */
        val acConfiguration = ACConfiguration.Builder()
            .setForceApplyGDPR(true)
            .setNeedToReplaceUrlViewerByQrCode(true)
            .build()

        AppConsentSDK.initialize(
            appKey = "YOUR_APPKEY",
            configuration = acConfiguration
        ) { appConsentInitialized ->
            /*
             To avoid certain problems, use the instance received by the onReady callback
             This has been successfully initialized
             */
            appConsent = appConsentInitialized
            registerCallbacks()
            tryToDisplayCmpAndCheckUpdateIfNeeded()
        }
    }

    private fun registerCallbacks() {
        /*
             Registers with CMP callback to know when the user
             has given consent, or if an error has occurred
             */
        appConsent?.setOnPresentNoticeListener(object : OnPresentNoticeListener {
            override fun presentConsentError(error: ACError) {
                /*
                 An error has occurred
                 */
                removeCMPCallback()
            }

            override fun presentConsentGiven() {
                /*
                The user has given his consent
                */
                removeCMPCallback()
                appConsent?.let { appConsentNN ->
                    updateFirebaseConsent(appConsentNN)
                }
            }
        })
    }

    private fun removeCMPCallback() {
        /*
        Not mandatory, but avoids keeping a local reference to the callback.
        Of course, it all depends on how your project is implemented.
        For example, with an SOP Activity, a singleton monitored by a flow, etc.
         */
        appConsent?.setOnPresentNoticeListener(null)
    }

    private fun updateFirebaseConsent(appConsent: AppConsent) {
        // Consent has just been validated by the user.
        // Recovers current GCM status (following user consent).
        val gcmConsentStatus = appConsent.getGCMConsentStatus()

        val newAnalyticsStorage =
            if (gcmConsentStatus.isAnalyticsStorageGranted) GRANTED else DENIED
        val newAdStorage =
            if (gcmConsentStatus.isAdStorageGranted) GRANTED else DENIED
        val newAdUserData =
            if (gcmConsentStatus.isAdUserDataGranted) GRANTED else DENIED
        val newAdPersonalization =
            if (gcmConsentStatus.isAdPersonalizationGranted) GRANTED else DENIED

        /*
        * We do not guarantee that Firebase will work with this example.
        * Please refer to the official documentation for details of initialization, re-initialization, reboot and other conditions.
        *
        * This example is only intended to show how to retrieve ConsentStatus and set it to your Firebase instance.
        */
        // Update Firebase status with information provided by the SDK
        Firebase.analytics.setConsent {
            analyticsStorage = newAnalyticsStorage
            adStorage = newAdStorage
            adUserData = newAdUserData
            adPersonalization = newAdPersonalization
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
import com.google.firebase.analytics.FirebaseAnalytics;
import com.sfbx.appconsent.AppConsent;
import com.sfbx.appconsent.core.model.gcm.GCMStatus;
import com.sfbx.appconsent.tv.AppConsentSDK;
import com.sfbx.appconsent.tv.listener.OnPresentNoticeListener;
import com.sfbx.appconsent.tv.model.ACConfiguration;
import com.sfbx.appconsent.tv.model.error.ACError;

import java.util.HashMap;
import java.util.Map;

import kotlin.Unit;

public class MainActivity extends AppCompatActivity {
    private AppConsent appConsent = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /*
        Shows whether the SDK has already been initialized
         */
        if (!AppConsentSDK.isSdkInitialized() || AppConsentSDK.getInstance() == null) {
            initCmpModule();
        } else {
            if (appConsent == null) {
                /*
                Retrieves the AppConsent instance or null if it has not yet been instantiated, for example
                 */
                appConsent = AppConsentSDK.getInstance();
            }
            tryToDisplayCmpAndCheckUpdateIfNeeded();
        }
    }

    /**
     * The only conditions for using this method would be if:
     * - you have configured your Notice by selecting this option: When saving changes to the notice, display the notice to all visitors.
     * - you plan to update your Notice often (more often than the : Consent retention period configurable in your Notice)
     * - Your users rarely restart your application.
     *
     * In which case, using this method from time to time may be a solution.
     *
     * But we encourage you to let the SDK handle this part on its own.
     */
    private void checkIfNoticeHasBeenUpdated() {
        appConsent.checkForUpdate(isNeedToPresentTheCmp -> {
            // Your Notice has been updated, you must represent the CMP to your users
            if (true == isNeedToPresentTheCmp) {
                /*
                Deletes old user consent locally.
                This step is not mandatory, but it avoids the need to make another network call
                to check whether the Notice has been updated,
                as no consent will be present on the user's device.
                 */
                appConsent.clearConsent();

                                /*
                Remember to re-register for callbacks if you have previously removed them,
                otherwise you will not know when the user has given their consent.
                 */
                registerCallbacks();
                if (false == tryToDisplayCMP()) {
                                           /*
                         At this stage, if the CMP is not displayed, it is possible, for example:
                         that the user is no longer in a geographical area subject to GDPR
                         */
                    removeCMPCallback();
                }
            } else {
                /*
                The Notice is the same as when it was last checked
                (you have made no changes since the board or
                it has not been updated internally by us, e.g. by updating a vendor)
                 */
                removeCMPCallback();
            }
            return Unit.INSTANCE;
        }, error -> {
            /*
             An error has occurred
             */
            removeCMPCallback();
            return Unit.INSTANCE;
        });
    }

    /**
     * Try to display the CMP
     *
     * @return true, if the CMP displaying, false otherwise
     */
    private Boolean tryToDisplayCMP() {
        /*
         Try to display the CMP according to certain rules.
         */
        return (appConsent.tryToDisplayNotice(false) == true);
    }

    private void tryToDisplayCmpAndCheckUpdateIfNeeded() {
        /*
         Try to display the CMP according to certain rules first.
         */
        if (false == tryToDisplayCMP()) {
            /*
             The user has already given consent;
             The user is not part of an area subject to the application of GDPR;
             etc.
             */
            removeCMPCallback();

            /*
            The Notice has not been displayed,
            so we'll have to check whether it has been updated
            and whether it needs to be shown to users again.
             */
            checkIfNoticeHasBeenUpdated();
        }
    }

    /*
     Initializes the consent management platform module when the activity is created
     */
    private void initCmpModule() {
        /*
        Add an error listener logger
         */
        AppConsentSDK.setErrorLogListener(appConsentTVError -> Log.e(MainActivity.class.toString(), appConsentTVError.toString()));

        /*
        Add an information listener logger
         */
        AppConsentSDK.setInformationLogListener(appConsentTVLog -> Log.i(MainActivity.class.toString(), appConsentTVLog.toString()));

        /*
        Add a navigation listener logger (historical)
         */
        AppConsentSDK.setNavigationLogListener(() -> Log.i(MainActivity.class.toString(), "onNoticeBackPressed"));

        /*
         ACConfiguration is used to configure the CMP.
         In this example:
         - We set forceApplyGDPR to true to display the CMP regardless of the user's region.
         - We set setNeedToReplaceUrlViewerByQrCode to true to display QR codes instead of the embedded webview for hyperlinks.
         */
        final ACConfiguration acConfiguration = new ACConfiguration.Builder()
                .setForceApplyGDPR(true)
                .setNeedToReplaceUrlViewerByQrCode(true)
                .build();

        AppConsentSDK.initialize(
                "YOUR_APPKEY",
                acConfiguration,
                appConsentInitialized -> {
                    /*
                     To avoid certain problems, use the instance received by the onReady callback
                     This has been successfully initialized
                     */
                    appConsent = appConsentInitialized;
                    registerCallbacks();
                    tryToDisplayCmpAndCheckUpdateIfNeeded();
                    return Unit.INSTANCE;
                });
    }

    private void registerCallbacks() {
        /*
                     Registers with CMP callback to know when the user
                     has given consent, or if an error has occurred
                     */
        appConsent.setOnPresentNoticeListener(new OnPresentNoticeListener() {
            @Override
            public void presentConsentError(@NonNull ACError acError) {
                            /*
                             An error has occurred
                             */
                removeCMPCallback();
            }

            @Override
            public void presentConsentGiven() {
                            /*
                             The user has given his consent
                             */
                removeCMPCallback();
                if (appConsent != null) {
                    updateFirebaseConsent(appConsent);
                }
            }
        });
    }

    private void removeCMPCallback() {
        /*
        Not mandatory, but avoids keeping a local reference to the callback.
        Of course, it all depends on how your project is implemented.
        For example, with an SOP Activity, a singleton monitored by a flow, etc.
         */
        if (appConsent != null) {
            appConsent.setOnPresentNoticeListener(null);
        }
    }

    private void updateFirebaseConsent(AppConsent appConsent) {
        // Consent has just been validated by the user.
        // Recovers current GCM status (following user consent).
        final GCMStatus gcmConsentStatus = appConsent.getGCMConsentStatus();

        final FirebaseAnalytics.ConsentStatus newAnalyticsStorage;
        if (gcmConsentStatus.isAnalyticsStorageGranted()) newAnalyticsStorage = GRANTED;
        else newAnalyticsStorage = DENIED;
        final FirebaseAnalytics.ConsentStatus newAdStorage;
        if (gcmConsentStatus.isAdStorageGranted()) newAdStorage = GRANTED;
        else newAdStorage = DENIED;
        final FirebaseAnalytics.ConsentStatus newAdUserData;
        if (gcmConsentStatus.isAdUserDataGranted()) newAdUserData = GRANTED;
        else newAdUserData = DENIED;
        final FirebaseAnalytics.ConsentStatus newAdPersonalization;
        if (gcmConsentStatus.isAdPersonalizationGranted()) newAdPersonalization = GRANTED;
        else newAdPersonalization = DENIED;

        /*
         * We do not guarantee that Firebase will work with this example.
         * Please refer to the official documentation for details of initialization, re-initialization, reboot and other conditions.
         *
         * This example is only intended to show how to retrieve ConsentStatus and set it to your Firebase instance.
         */
        // Update Firebase status with information provided by the SDK
        final Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentStatus = new HashMap<>();
        consentStatus.put(FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE, newAnalyticsStorage);
        consentStatus.put(FirebaseAnalytics.ConsentType.AD_STORAGE, newAdStorage);
        consentStatus.put(FirebaseAnalytics.ConsentType.AD_USER_DATA, newAdUserData);
        consentStatus.put(FirebaseAnalytics.ConsentType.AD_PERSONALIZATION, newAdPersonalization);
        FirebaseAnalytics.getInstance(getApplicationContext()).setConsent(consentStatus);
    }
}
```

{% endtab %}
{% endtabs %}


# FAQ

***

## Android Context has not been loaded

```
********************************************************************************************************
* The Android context has not been loaded by the [androidx.startup.Initializer] plugin.                *
* If you encounter this error, please let us know through support so that we can improve the product.  *
* To resolve this error, call the [AppConsentSDK.loadContext] method.                                  *
********************************************************************************************************
```

If this error appears in your logs, follow the instruction recommended by the message by calling the following method :

`AppConsentSDK.loadContext`

Once you've done this, call the `AppConsentSDK.initialize` method again, as you did initially.

## RuntimeException caused by WebView

```
Caused by: java.lang.RuntimeException:
Using WebView from more than one process at once with the same data directory is not supported.
https://crbug.com/558377 : Current process xx.xxx.xxxx (pid aaaaa), lock owner\"yy.yyy.yyyy (pid bbbbb)
```

First of all, look for the cause. It may be that this happens when you integrate our SDK (we use the WebView component), but the problem arises because the component is used by different memory areas (thread, process).

Multiple instances cannot access the same files / folders stored on the device from different Threads (concurrency).

The SDK manages this problem, but it may not be able to correct it if, for example, a third-party library uses the WebView component before the SDK has even initialized.

In which case, you'll have to manage this part yourself by defining a new directory for your WebView (<https://crbug.com/558377>) using this method:

`WebView.setDataDirectorySuffix(String)`

## Duplicate classes

When compiling your project, you encounter an error from the kotlin.stdlib library explaining that you have a duplicate class ?

```
Caused by: java.lang.RuntimeException: Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules kotlin-stdlib-1.8.20-dev-78 (org.jetbrains.kotlin:kotlin-stdlib:1.8.20-dev-78) and kotlin-stdlib-jdk8-1.5.30 (org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30)
```

Add this configuration block to your app module build.gradle:

```
dependencies {
    ...
    modules {
        module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") {
            replacedBy("org.jetbrains.kotlin:kotlin-stdlib", "kotlin-stdlib-jdk7 is now part of kotlin-stdlib")
        }
        module("org.jetbrains.kotlin:kotlin-stdlib-jdk8") {
            replacedBy("org.jetbrains.kotlin:kotlin-stdlib", "kotlin-stdlib-jdk8 is now part of kotlin-stdlib")
        }
    }
    ...
}
```

## Decompilation error

If you are using a version of gradle prior to version 8, you may encounter the following error when integrating CMP (since version 4.X.X):

`Error: com.android.tools.r8.internal.nc: Sealed classes are not supported as program classes`

This is a "normal" error, as Gradle 7 is unable to decompile certain classes (Gradle 8 requires you to migrate to JAVA 17).

The only solution is to add the R8 plugin to your project.

The reference for this patch comes from a [Google Issue](https://issuetracker.google.com/issues/290412574) and the solution is as follows:

```
pluginManagement {
    buildscript {
        repositories {
            mavenCentral()
            maven {
                url = uri("https://storage.googleapis.com/r8-releases/raw")
            }
        }
        dependencies {
            classpath("com.android.tools:r8:8.2.20-dev")

        }
    }
}
```


# iOS

1. [AppConsent SDK Documentation](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-sdk)
2. [AppConsentUnified SDK Documentation](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk)


# AppConsent Unified SDK

1. [Get Started](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/step-1-get-started)
2. [Basic Integration](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/step-2-basic-integration)
3. [Advanced Integration](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction)
4. [Go further](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further)
   1. [Advanced Interaction](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction)
   2. [Data Access](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction/data-access)
   3. [Special Actions](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction/specific-actions)
5. [Logging](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/logging)
6. [App Tracking Transparency](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/app-tracking-transparency)


# Step 1: Get Started

Dependency Resolution Management

### Swift Package Manager <a href="#swift-package-manager" id="swift-package-manager"></a>

To install AppConsentUnified using [Swift Package Manager](https://github.com/apple/swift-package-manager) you can follow the [tutorial published by Apple](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app) using the URL for this repo with the current version:

1. In Xcode, select “File” → “Add Packages...”
2. Enter <https://gitlab.datalf.chat/customers/appconsent-ios-unified.git>

or you can add the following dependency to your Package.swift:

```swift
.package(url: "https://gitlab.datalf.chat/customers/appconsent-ios-unified.git", from: "1.0.0" )
```

and add it to your target like this:

```swift
dependencies: [
  .package(url: "https://gitlab.datalf.chat/customers/appconsent-ios-unified.git", from: "1.0.0")
]
```

### Drag & Drop XCFramework <a href="#drag-drop-xcframework" id="drag-drop-xcframework"></a>

Download AppConsentUnified XCFramework from the AppConsentUnified repository.

AppConsentUnified <https://gitlab.datalf.chat/customers/appconsent-ios-unified>

In General tab of your application target, drag and drop the **`AppConsentUnified.xcframework`** in Frameworks, Libraires and Embedded Content. \
\
Make sure the XCFrameworks are Embed & Sign.

<br>


# Step 2: Basic Integration

***

## How to use AppConsentUnified

### 1. Get your AppKey

On AppConsent configuration interface [https://app.appconsent.io](< https://app.appconsent.io>), create your unified SDK source.

<figure><img src="/files/MoGc4k3kexUz4EUXOfQg" alt=""><figcaption></figcaption></figure>

Then  create your notice and retrieve your generated **YOUR\_APP\_KEY**&#x20;

<figure><img src="/files/ETbXFzlfMoR29FVrzSmm" alt=""><figcaption></figcaption></figure>

### 2. Initialise AppConsentUnified

The second step is to initialise the SDK\
\
Our AppConsentUnified iOS SDK supports the following target:

* iOS, (iPhone, iPad and macOS through mac Catalyst) minimum deployment target 13.0

Our AppConsentUnified framework is built in Swift and UIKit, and supported integration within a Swift and Objective-C app.  It is possible to use within a SwiftUI app following various approaches (one of them is in the example below)

{% hint style="warning" %}
**INFO**\
AppConsentUnified SDK supports the new App Tracking Transparency framework (> iOS 14+ available). You must register the **NSUserTrackingUsageDescription key in your Info.plist** of your application otherwise your app will crash. \
\
See [App Tracking Transparency](https://docs.sfbx.io/ios-api-reference/documentation/appconsent/apptrackingtransparency) for details.
{% endhint %}

{% tabs %}
{% tab title="UIKit (Swift)" %}
{% hint style="info" %}
**INFO**

In the example below, the code focuses on using the SDK **exclusively**.
{% endhint %}

<pre class="language-swift"><code class="lang-swift">import AppConsentUnified

class ExampleViewController: UIViewController {

   // AppConsent Object
    private(set) var appConsent: ACNotice!
    
<strong>    override func viewDidLoad() {
</strong>        super.viewDidLoad()
       
        self.appConsent = ACNotice(withAppKey: "YOUR_APP_KEY")
                
        // Present CMP when you need it (it can after some internal logic on your end not necessarely here as the example shows)
        self.appConsent.presentNotice(viewController: self)
    }
}   
</code></pre>

{% endtab %}

{% tab title="UIKit (Objective-C)" %}
{% hint style="info" %}
**INFO**\
\
In the example below, the code focuses on using the SDK **exclusively**.
{% endhint %}

First import our Swift header declaration&#x20;

```objectivec
#import <AppConsentUnified/AppConsentUnified-Swift.h>
#import <AppConsentUnified/ACNotice.h>
```

If you are mixing Swift + Objective-C you will need to important as well your `"App-Swift.h"` file. \
\
For example:&#x20;

```objectivec
#import "YourAppModule-Swift.h"
```

Once this is done, here below a basic implementation:

```objectivec
#import <UIKit/UIKit.h>
#import <AppConsentUnified/ACNotice.h>
#import <AppConsentUnified/AppConsentUnified-Swift.h>

NS_ASSUME_NONNULL_BEGIN
@interface ExampleObjcViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
```

Create a property ACNotice in your implementation file (.m)

```objectivec
#import "ExampleObjcViewController.h"

@interface ExampleObjcViewController ()
@property(nonatomic, strong) ACNotice *appConsent;
@end

@implementation ExampleObjcViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // initialise notice
    self.appConsent = [[ACNotice alloc] initWithAppKey:@"YOUR_APP_KEY"];
    // Present Notice
    [self.appConsent presentNoticeWithViewController:self];
}
@end

```

{% endtab %}

{% tab title="SwiftUI" %}

{% hint style="info" %}
**INFO**\
\
Our SDK is developed with UIKit therefore it requires some adjustments for SwiftUI. \
\
For this example below we showcased an integration using top keyWindow. In order to achieve it, some extension class have been created.\
\
You can other ways of integrating our SDK, for example using **UIViewRepresentable**.&#x20;
{% endhint %}

*(Optional)* First create a ViewModel in order to encapsulate all business logic from our SDK into a view model that conform to observable protocol. Later this object will be use within the view&#x20;

```swift
final class AppConsentViewModel: ObservableObject {
    
    private(set) var appConsent: ACNotice

    init(ac: ACNotice) {
        self.appConsent = ac
    }
    
    func presentNotice() {
        if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
            appConsent.presentNotice(viewController: rootVC)
        } else {
            noticeError = NSError(domain: "your_bundle_id", 
            code: 999, // Choose your internal code if needed
            userInfo: [
            NSLocalizedFailureReasonErrorKey: 
            "Unable to present notice, keyWindow is nil"
            ])
        }
    }
}
```

*(Optional)* If you are using Scene Delegate or Scenes, then (if not already done on your end) create a small extension in order to be able to grab the proper keyWindow.

```swift
extension UIApplication {
    var keyWindow: UIWindow? {
        if #available(iOS 15.0, *) {
            return connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .first(where: { $0.activationState == .foregroundActive })?
                .windows
                .first(where: { $0.isKeyWindow })
        } else {
            // iOS 13–14 fallback
            return windows.first { $0.isKeyWindow }
        }
    }
}
```

Your final View should looks like this and is fully implemented&#x20;

```swift
import SwiftUI
import AppConsentUnified

struct ContentView: View {
    @StateObject private var appConsentViewModel: AppConsentViewModel
    
    init(appConsentViewModel: AppConsentViewModel) {
        self._appConsentViewModel = StateObject(wrappedValue: appConsentViewModel)
    }
    var body: some View {
        VStack {
            // YOUR VIEW ELEMENTS
            Text("My View")
        }.onAppear {
            let appConsent = ACNotice(withAppKey: "YOUR_APP_KEY")
            // present notice
            appConsentViewModel.presentNotice()
        }
    }
}
```

{% endtab %}
{% endtabs %}


# Step 3: Advanced Integration

***

## How to use appConsent object ?

### **Specify a dedicated endpoint at init**

{% tabs %}
{% tab title="Swift" %}

```swift
let appConsent = ACNotice(withAppKey: "YOUR_APP_KEY", dedicatedEndpointURL: "YOUR_BACKEND_ENDPOINT")
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
 self.appConsent = [[ACNotice alloc] initWithAppKey:@"YOUR_APP_KEY" dedicatedEndpointURL:@"YOUR_BACKEND_ENDPOINT"];
```

{% endtab %}
{% endtabs %}

### **Redirecting user to their Privacy Settings**

This screen displays the user consent details and allows granular control, enabling users to provide consent for each individual item. User can also modify existing consent in that settings page.

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.presentSettings(viewController: self)
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
[self.appConsent presentSettingsWithViewController:self];
```

{% endtab %}
{% endtabs %}

### **Check if user gave consent**

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.consentGiven()
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
BOOL consentStatus = [appConsent consentGiven];
```

{% endtab %}
{% endtabs %}

Return `true` if consent is given, `false` otherwise.

{% hint style="danger" %}
**WARNING**

Please note that we are only talking about whether the user has confirmed a **choice** and not whether he has **accepted** or **refused** consent.
{% endhint %}

### **Implement delegates related to user consent**

When the user has completed the consent process and given their consent, this delegate informs you whether our CMP did finish successfully or did fail. Depending on these delegate you can trigger some action on your end depending on your business logic

{% tabs %}
{% tab title="UIKit (Swift)" %}
{% hint style="info" %}
**INFO**

In the example below, we start with the previous basic example found at [Step 2: Basic Integration](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/step-2-basic-integration)
{% endhint %}

Assign ACNotice delegate to self

```swift
  self.appConsent.delegate = self
```

Add an extension to your ViewController that conform to that Delegate&#x20;

```swift
extension ExampleViewController: AppConsentDelegate {
    
    func appConsentDidFinish() {
       // Below you can place your own business logic, for example grab the general consent status
        print("🔥 AppConsentUnified Delegate: did finish")
        
        let consentStatus = appConsent.consentStatus()
        print("🔥 AppConsentUnified Delegate: consent status => \(consentStatus.description)")
    }
    
    func appConsentDidFail(_ error: Error?) {
        print("🔥 AppConsentUnified Delegate: did fail, \(error?.localizedDescription ?? "No error details")")
    }
    
    func appConsentDidAppear() {
        print("🔥 AppConsentUnified Delegate: did appear")
    }
    
    func appConsentDidDisappear() {
        print("🔥 AppConsentUnified Delegate: did disappear")
    }
}
```

Full View Controller implementation:&#x20;

```swift
import AppConsentUnified

class ExampleViewController: UIViewController {

   // Notice Object
    private(set) var notice: ACNotice!
    
    override func viewDidLoad() {
        super.viewDidLoad()
       
        self.appConsent = ACNotice(withAppKey: "YOUR_APP_KEY")
        
        // Delegate is used to get some callbacks when CMP finishes its execution
        self.appConsent.delegate = self
        
        // Present CMP when you need it (it can after some internal logic on your end not necessarely here as the example shows)
        self.notice.presentNotice(viewController: self)
    }
}

fileprivate extension ExampleViewController: AppConsentDelegate {
    
    func appConsentDidFinish() {
       // Below you can place your own business logic, for example grab the general consent status
        print("🔥 AppConsentUnified Delegate: did finish")
        
        let consentStatus = notice.consentStatus()
        print("🔥 AppConsentUnified Delegate: consent status => \(consentStatus.description)")
    }
    
    func appConsentDidFail(_ error: Error?) {
        print("🔥 AppConsentUnified Delegate: did fail, \(error?.localizedDescription ?? "No error details")")
    }
    
    func appConsentDidAppear() {
        print("🔥 AppConsentUnified Delegate: did appear")
    }
    
    func appConsentDidDisappear() {
        print("🔥 AppConsentUnified Delegate: did disappear")
    }
}
   
```

{% endtab %}

{% tab title="UIKit (Objective-C)" %}
{% hint style="info" %}
**INFO**\
\
In the example below, we start with the previous basic example found at [Step 2: Basic Integration](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/step-2-basic-integration)
{% endhint %}

\
Implement AppConsentDelegate in your header ile (.h)

```objectivec
#import <UIKit/UIKit.h>
#import <AppConsentUnified/ACNotice.h>
#import <AppConsentUnified/AppConsentUnified-Swift.h>

NS_ASSUME_NONNULL_BEGIN
@interface ExampleObjcViewController : UIViewController <AppConsentDelegate>
@end
NS_ASSUME_NONNULL_END
```

Assign ACNotice delegate to self&#x20;

```objectivec
[self.appConsent setDelegate:self];
```

Then implement the delegate methods in your implementation file (.m) as per below

```objectivec
- (void)appConsentDidFail:(NSError * _Nullable)error {
   NSLog(@"🔥 AppConsentUnified Delegate: did fail %@", error.localizedDescription);
}

- (void)appConsentDidFinish {
    NSLog(@"🔥 AppConsentUnified Delegate: did finish");
    
    // Here you can place your own business logic, for example grab the general consent status
    ACConsentStatus consentStatus = [self.appConsent consentStatus];
    
    switch (consentStatus) {
        case ACConsentStatusAllowed:
            NSLog(@"🔥 AppConsentUnified Delegate: consent status Allowed");
            break;
        case ACConsentStatusDenied:
            NSLog(@"🔥 AppConsentUnified Delegate: consent status Denied");
        case ACConsentStatusMixed:
            NSLog(@"🔥 AppConsentUnified Delegate: consent status Mixed. Need to verify per purposes");
        default:
            break;
    }
}

-(void)appConsentDidAppear {
    NSLog(@"🔥 AppConsentUnified Delegate: did appear");
}

- (void)appConsentDidDisappear {
    NSLog(@"🔥 AppConsentUnified Delegate: did disappear");
}
```

**Full implementation ViewController implementation**

`ExampleObjcViewController.h`

```objectivec
#import <UIKit/UIKit.h>
#import <AppConsentUnified/ACNotice.h>
#import <AppConsentUnified/AppConsentUnified-Swift.h>

NS_ASSUME_NONNULL_BEGIN

@interface ExampleObjcViewController : UIViewController <AppConsentDelegate>
@end

NS_ASSUME_NONNULL_END

```

**ExampleObjcViewController.m**

```objectivec
#import "ExampleObjcViewController.h"

@interface ExampleObjcViewController ()
@property(nonatomic, strong) ACNotice *appConsent;
@end

@implementation ExampleObjcViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // initialise notice
    self.appConsent = [[ACNotice alloc] initWithAppKey:@"YOUR_APP_KEY"];
     // Set Delegate
     [self.appConsent setDelegate:self];
    // Present Notice
    [self.appConsent presentNoticeWithViewController:self];
}

- (void)appConsentDidFail:(NSError * _Nullable)error {
   NSLog(@"🔥 AppConsentUnified Delegate: did fail %@", error.localizedDescription);
}

- (void)appConsentDidFinish {
    NSLog(@"🔥 AppConsentUnified Delegate: did finish");
    
    // Here you can place your own business logic, 
    // An example here: get the general consent status
    ACConsentStatus consentStatus = [self.appConsent consentStatus];
    
    switch (consentStatus) {
        case ACConsentStatusAllowed:
            NSLog(@"🔥 AppConsentUnified Delegate: consent status Allowed");
            break;
        case ACConsentStatusDenied:
            NSLog(@"🔥 AppConsentUnified Delegate: consent status Denied");
        case ACConsentStatusMixed:
            NSLog(@"🔥 AppConsentUnified Delegate: consent status Mixed. Need to verify per purposes");
        default:
            break;
    }
}

-(void)appConsentDidAppear {
    NSLog(@"🔥 AppConsentUnified Delegate: did appear");
}

- (void)appConsentDidDisappear {
    NSLog(@"🔥 AppConsentUnified Delegate: did disappear");
}
@end

```

{% endtab %}

{% tab title="SwiftUI" %}
{% hint style="info" %}
**INFO**\
\
In the example below, we start with the previous basic example found at [Step 2: Basic Integration](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/step-2-basic-integration)
{% endhint %}

Conform your ViewModel to `AppConsentDelegate` as per below

```swift

// AppConsent Delegate
fileprivate extension AppConsentViewModel: AppConsentDelegate {    
    func appConsentDidFinish() {
        print("🔥 AppConsentUnified Delegate: did finish")
        noticeCompleted = true
    }
    func appConsentDidFail(_ error: Error?) {
        print("🔥 AppConsentUnified Delegate: did fail, \(error?.localizedDescription ?? "No error details")")
        noticeError = error
    }
    
    func appConsentDidAppear() {
        print("🔥 AppConsentUnified Delegate: did appear")
    }
    
    func appConsentDidDisappear() {
        print("🔥 AppConsentUnified Delegate: did disappear")
    }
}
```

Add 2 published properties on your ViewModel in order to react on delegates&#x20;

```swift
final class AppConsentViewModel: ObservableObject {
    
    private(set) var appConsent: ACNotice
    @Published var noticeCompleted = false
    @Published var noticeError: Error?
    
    init(ac: ACNotice) {
        self.appConsent = ac
    }
    
    func presentNotice() {
        if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
            // Set delegate
            appConsent.delegate = self
            // Present Notice
            appConsent.presentNotice(viewController: rootVC)
        } else {
            noticeError = NSError(domain: "your_bundle_id", 
            code: 999, // Choose your internal code if needed
            userInfo: [
            NSLocalizedFailureReasonErrorKey: 
            "Unable to present notice, keyWindow is nil"
            ])
        }
    }
}
```

Full View Model implementation\ <br>

```swift
final class AppConsentViewModel: ObservableObject {
    private(set) var appConsent: ACNotice
    
    @Published var noticeCompleted = false
    @Published var noticeError: Error?
    
    init(notice: ACNotice) {
        self.appConsent = notice
    }
        
    func presentNotice() {
        if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
            appConsent.delegate = self
            appConsent.presentNotice(viewController: rootVC)
        } else {
            noticeError = NSError(domain: "your_bundle_id", code: 999, userInfo: [NSLocalizedFailureReasonErrorKey: "Unable to present notice, keyWindow is nil"])
        }
    }
}

// AppConsent Delegate
extension AppConsentViewModel: AppConsentDelegate {
    
    func appConsentDidFinish() {
        print("🔥 AppConsentUnified Delegate: did finish")
        noticeCompleted = true
    }
    
    func appConsentDidFail(_ error: Error?) {
        print("🔥 AppConsentUnified Delegate: did fail, \(error?.localizedDescription ?? "No error details")")
        noticeError = error
    }
    
    func appConsentDidAppear() {
        print("🔥 AppConsentUnified Delegate: did appear")
    }
    
    func appConsentDidDisappear() {
        print("🔥 AppConsentUnified Delegate: did disappear")
    }
}
```

SwiftUI View implementation `AppConsentViewModel`  to receive delegates events

```swift
import SwiftUI
import AppConsentUnified

struct ContentView: View {
    @StateObject private var appConsentViewModel: AppConsentViewModel
    
    init(appConsentViewModel: AppConsentViewModel) {
        self._appConsentViewModel = StateObject(wrappedValue: appConsentViewModel)
    }
    var body: some View {
        VStack {
            // YOUR VIEW ELEMENTS
            Text("My View")
        }.onAppear {
            let appConsent = ACNotice(withAppKey: "YOUR_APP_KEY")
            // present notice
            appConsentViewModel.presentNotice()
        }.onReceive(appConsentViewModel.$noticeCompleted) { success in
            // Here you can place your own business logic, for example grab the general consent status
            let consentStatus = appConsentViewModel.appConsent.consentStatus()
            print("🔥 AppConsentUnified Delegate: did finish consent status => \(consentStatus.description)")
        }.onReceive(appConsentViewModel.$noticeError) { error in
            // Here you can place your own business logic for error handling
        }
    }
}
```

{% endtab %}
{% endtabs %}

### **Check for update**

This method allows you to check from our servers whether your Notice has been updated since it was last displayed on your user's device.

{% hint style="info" %}
**INFO**

The method will return true if you have modified the Source and/or Notice from your dashboard and, if and **only if**, you have configured your Notice to update for all your users.
{% endhint %}

<figure><img src="/files/9qusg58AojkqxJkrT4ZL" alt=""><figcaption></figcaption></figure>

\
Our latest SDK supports both traditional callback-based APIs and the modern Swift concurrency approach using async/await for most methods. \
\
**Check for update - callback-based**

{% tabs %}
{% tab title="Swift" %}

```swift
  appConsent.checkForUpdate { [weak self] needUpdate in
             if needUpdate {
                // Your Notice has been updated, you must represent the CMP to your users
                // You can call presentNotice as per the below example
                 guard let self else { return }
                 appConsent.presentNotice(viewController: self)
            }
        }
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
    __weak typeof(self) weakSelf = self;
    
       [self.notice checkForUpdate:^(BOOL needUpdate) {
        if (needUpdate) {
            __strong typeof(self) strongSelf = weakSelf;
            if (!strongSelf) return;
            // Your Notice has been updated, you must represent the CMP to your users
           // You can call presentNotice as per the below example
            [self.notice presentNoticeWithViewController:strongSelf];
        }
    }];
```

{% endtab %}
{% endtabs %}

**Check for update - Modern concurrency**

{% tabs %}
{% tab title="Swift" %}

```swift
await appConsent.checkForUpdate()
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

### **GCM Status**

{% hint style="info" %}
**INFO**

This method shows the **current** status of GCMv2 (Google Consent Mode V2).

Before calling up this method, it's best to make sure that the user has **already given his consent** and that it's up to date, and that the CMP doesn't need to be redisplayed.

Otherwise :

* either the saved value of the **old consent** will be returned
* or the **default values** of your FirebaseAnalytics Info.plist configuration file will be returned

[Set the default consent state from google documentation](https://developers.google.com/tag-platform/security/guides/app-consent?consentmode=advanced\&platform=ios\&hl=fr#default-consent)
{% endhint %}

:information\_source: As some annotations are not compatible with Objective-C, we do have separate method for Objective-C and Swift.

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.gcmStatus()
```

Example:&#x20;

```swift
let gcmStatus: GCMStatus = appConsent.gcmStatus()
```

Return `GCMStatus`
{% endtab %}

{% tab title="Objective-C" %}

```objectivec
[self.appConsent objcGcmStatus];
```

Example:&#x20;

```objectivec
GCMObjCStatus* gcmStatus = [self.appConsent objcGcmStatus];
```

Return `GCMObjCStatus`
{% endtab %}
{% endtabs %}

<details>

<summary>Data model representing the state of GCMv2 (Google Consent Mode v2)</summary>

This will allow you to define consent from your Firebase Analytics &#x20;

1. Set the following key in your Info.plist file:
   * `GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE`
   * `GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE`
   * `GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA`
   * `GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS`

For example, to set all grant consent for all parameters by default:

```
<key>GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE</key> <true/>
<key>GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE</key> <true/>
<key>GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA</key> <true/>
<key>GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS</key> <true/>
```

</details>


# Go further

1. [Advanced data methods](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction)
2. [Logging](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/logging)
3. [App Tracking Transparency](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/app-tracking-transparency)


# Advanced Interaction

## Using AppConsent's more specific methods

### [1. Data Access](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction/data-access)

### [2. Specific Actions](/configuration/step-3-notice-implementation-web-app-tv/ios/appconsent-unified-sdk/go-further/advanced-interaction/specific-actions)


# Data Access

Here's a list of methods that could be useful if you want to go further in tracking user consent.

### **Purpose allowed**

```swift
func purposeAllowed(iabId: UInt32) -> Bool
```

Return `true` if purpose with `iabId`  is allowed, `false` if propose is not in the allowed purpose list.\
\
&#x20;:warning:  When this method return `false` does not mean this purpose is disallowed. \
\
If you receive a `false` , you can fetch all purposes `func getAllPurposes() -> [ConsentCollection]?`   and filter them based on your purpose's `iadId` to get extra information about the consent status.

### **Extra Purpose allowed**

```swift
func extraPurposeAllowed(iabId: String) -> Bool
```

Return `true` if extra purpose with `iabId`  is allowed, `false` if this extra purpose is not in the allowed extra purpose list.

&#x20;:warning:  When this method return `false` it does not mean this extra purpose is disallowed. \
\
If you receive a `false`, you can fetch all  extra purposes  `func getAllExtraPurposes() -> [ConsentCollection]?` and filter them based on your extra purposes `iadId` in order to get extra information about the consent status.

### **Feature allowed**

```swift
func featureAllowed(iabId: UInt32) -> Bool
```

Return `true` if feature with `iabId`  is allowed, `false` if this feature is not in the allowed  feature list.

&#x20;:warning:  When this method return `false` it does not mean this feature is disallowed. \
\
If you receive a `false`, you can fetch all  all features `func getAllFeatures() -> [ConsentCollection]?` and filter them based on your feature `iadId` in order to get extra information about the consent status.

### **Stack allowed**

```swift
func stackAllowed(iabId: UInt32) -> Bool 
```

Return `true` if stack with `iabId`  is allowed, `false` if this stack is not in the allowed stack list.\
\
&#x20;:warning:  When this method return `false` it does not mean this stack is disallowed. \
\
If you receive a `false` , you can fetch all stacks `func getAllStacks() -> [ConsentCollection]?`  and filter them based on your stack `iadId` in order to get extra information about the consent status.

### **Vendor allowed**

```swift
 func vendorAllowed(iabId: UInt32) -> Bool
```

Return `true` if vendor with `iabId` is allowed, `false` if this vendor is not in the allowed vendor list.

&#x20;:warning:  When this method return `false` it does not mean this vendor is disallowed. \
\
If you receive a `false`, you can fetch all vendors `func getAllVendors() -> [ConsentCollection]?` and filter them based on your vendor `iadId` in order to get extra information about the consent status.

### **Extra Vendor allowed**

```swift
func extraVendorAllowed(extraId: String) -> Bool 
```

Return `true` if extra vendor with `extraId`  is allowed, `false` if this extra vendor is not in the allowed extra vendor list.

&#x20;:warning:  When this method return `false` it does not mean this extra vendor is disallowed. \
\
If you receive a `false`, you can fetch all  extra vendors `func getExtraAllVendors() -> [ConsentCollection]?` and filter them based on your extra vendor `iadId` in order to get extra information about the consent status.

### **All Extra Vendors**

```swift
func getAllExtraVendors() -> [ConsentCollection]?
```

Return all consent informations for all extra vendors. \
\
`ConsentCollection`  payload has this data below (not exclusive)

```
{
    "id": "1",
    "type": "0",
    "status": "0",
}
```

### **All Vendors**

```swift
func getAllVendors() -> [ConsentCollection]?
```

Return all consent informations for all vendors. \
\
`ConsentCollection`  payload has this data below (not exclusive)

```
{
    "id": "1",
    "type": "0",
    "status": "0",
}
```

### **All Vendors Consent Status**

```swift
func allVendorStatus() -> ACConsentStatus
```

Returns a `ACConsentStatus` object representing the overall consent status of your vendors. The consent status is defined as an enum with the following possible values:

```
pending = 0
allowed = 1
mixed = 2
denied = -1
undefined = -2
```

### **All Stack Consent Status**

```swift
func allStacksStatus() -> ACConsentStatus
```

Returns a `ACConsentStatus` object representing the overall consent status of your stacks. The consent status is defined as an enum with the following possible values:

```
pending = 0
allowed = 1
mixed = 2
denied = -1
undefined = -2
```

### **All Purposes**

```swift
func getAllPurposes() -> [ConsentCollection]?
```

Return all consent informations for all purposes. \
\
`ConsentCollection`  payload has this data below (not exclusive)

```
{
    "id": "1",
    "type": "0",
    "status": "0", // 1 if consent, 0 if denied
}
```

### **All Purposes filtered by Consent Status**

```swift
func getAllPurposes(by status: ACConsentStatus) -> [ConsentCollection]?
```

Return all consent informations for all purposes filtered by Consent Status (pending = 0,  allowed = 1, mixed = 2, denied = -1, undefined = -2)

### **All Purposes Consent Status**

```swift
func allPurposesStatus() -> ACConsentStatus
```

Returns a `ACConsentStatus` object representing the overall consent status of your purposes. The consent status is defined as an enum with the following possible values:

```
pending = 0
allowed = 1
mixed = 2
denied = -1
undefined = -2
```

### **All Features**

```swift
func getAllFeatures() -> [ConsentCollection]?
```

Return all consent informations for all features. \
\
`ConsentCollection`  payload has this data below (not exclusive)

```
{
    "id": "1",
    "type": "0",
    "status": "0", // 1 if consent, 0 if denied
}

```

### **All Special Features**

```swift
func getAllSpecialFeatures() -> [ConsentCollection]?
```

Return all consent informations for all features. \
\
`ConsentCollection`  payload has this data below (not exclusive)

```
{
    "id": "1",
    "type": "0",
    "status": "0", // 1 if consent, 0 if denied
}

```

### **All Stacks**

```swift
func getAllStacks() -> [ConsentCollection]?
```

Return all consent informations for all stacks. \
\
`ConsentCollection`  payload has this data below (not exclusive)

```
{
    "id": "1",
    "type": "0",
    "status": "0", // 1 if consent, 0 if denied
}
```

### **All Stacks filtered by Consent Status**

```swift
func getAllStacks(by status: ACConsentStatus) -> [ConsentCollection]?
```

Return all consent informations for all stacks filtered by Consent Status.

### **All Stacks Consent Status**

```swift
func allStacksStatus() -> ACConsentStatus
```

Returns a `ACConsentStatus` object representing the overall consent status of your stacks. The consent status is defined as an enum with the following possible values:

```
pending = 0
allowed = 1
mixed = 2
denied = -1
undefined = -2
```

### **General Consent Status**

```swift
 func consentStatus() -> ACConsentStatus 
```

Returns a `ACConsentStatus` object representing the general overall consent status. The consent status is defined as an enum with the following possible values:

```
pending = 0
allowed = 1
mixed = 2
denied = -1
undefined = -2
```


# Specific Actions

Here's a list of methods that could be useful if you want to interact further with our SDK (updating some data sets, overriding consent, etc)

### **Clear consent**

Locally removes user consent, but not on the server (this will allow a new display of the CMP on the next call to `presentNotice` for example)

```swift
func clearConsent() 
```

### **Set external ids**

Allows to define additional Ids that will be taken into account when validating user consent.

```swift
func setExternalIds(externalIds: [String: String]) -> Self
```

Example:&#x20;

{% tabs %}
{% tab title="Swift" %}

```swift
let externalsIds = ["customPersonalId": "abze43"]
appConsent.setExternalIds(externalIds: externalsIds)
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
  NSDictionary* infos = @{@"customPersonalId": @"abze43"};
  [self.appConsent setExternalIdsWithExternalIds:infos];
```

{% endtab %}
{% endtabs %}

Once save locally, you can call `saveExternalIds`  methods below to transmit the external ids.

### **Save external ids**

This method transmit all our previous externalIds saved to our servers.&#x20;

**callback-based**

```swift
func saveExternalIds(_ completion: ACTypedResultVoidHandler?)
```

Example:

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.saveExternalIds { result in
            switch result {
                case .success:
                print("🔥 Save External Ids Successful")
            case .failure:
                print("🔥 Save External Ids Failure")
            }
        }
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
  NSDictionary* data = [self.appConsent getExternalIds];
```

{% endtab %}
{% endtabs %}

**modern concurrency**

```swift
func saveExternalIds() async -> Result<Bool, NoticeError>
```

{% tabs %}
{% tab title="Swift" %}

```swift
let result = await appConsent.saveExternalIds()
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

### **Get external ids**

Retrieves your previously registered external ids

```swift
func getExternalIds() -> Dictionary<String, String>
```

{% tabs %}
{% tab title="Swift" %}

```swift
let externalIds: [String: String] = appConsent.getExternalIds()
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
  NSDictionary* data = [self.appConsent getExternalIds];
```

{% endtab %}
{% endtabs %}

### Overwrite **Consent Status**

This method allows you to overwrite a given user consent based on your internal business rules.&#x20;

It requires to build a `ConsentOverride`  object that contains multiples optional properties that allows you to override consent for specific elements (purposes, specialPurposes, vendors, etc).\
\
Each property require is a list of `ConsentStatus` that is constructed with on your element Id, the consent status and the legintStatus (optional, default value: `false`)\
\
C**allback-based**

{% tabs %}
{% tab title="Swift" %}

```swift
  let purposes = [
  ConsentStatus(id: 1, status: true),
  ConsentStatus(id: 3, status: true, legintStatus: false)
  ]
  
  let specialPurposes = [
        ConsentStatus(id: 19, status: true),
        ConsentStatus(id: 23, status: true, legintStatus: false)]
        
 let consent = ConsentOverride(
 purposes: purposes, 
 specialPurposes: specialPurposes
  )
        
 appConsent.overwriteConsent(consent: consent) { success in 
  // Success tells you whether the override consent has been applied or not
}
```

{% endtab %}

{% tab title="Objective-C" %}
**Not available**
{% endtab %}
{% endtabs %}

**Modern concurrency**<br>

{% tabs %}
{% tab title="Swift" %}

```swift
  let purposes = [
  ConsentStatus(id: 1, status: true),
  ConsentStatus(id: 3, status: true, legintStatus: false)
  ]
  
  let specialPurposes = [
        ConsentStatus(id: 19, status: true),
        ConsentStatus(id: 23, status: true, legintStatus: false)]
        
 let consent = ConsentOverride(
 purposes: purposes, 
 specialPurposes: specialPurposes
  )
        
 await appConsent.overwriteConsent(consent: consent)
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

### **Save floating purposes**

This method allows to save a consent on a floating purpose. &#x20;

**callback-based**

```swift
func saveFloatingPurpose(data: [String: Bool], completion: ACTypedResultVoidHandler?)
```

Example:

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.saveFloatingPurpose(data: ["customFloatingPurpose": true]) { result in}
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

**modern concurrency**

```swift
func saveFloatingPurpose(data: [String: Bool]) async -> Result<Bool, NoticeError>
```

{% tabs %}
{% tab title="Swift" %}

```swift
let result = await appConsent.saveFloatingPurpose(data: ["customFloatingPurpose": true])
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

### **Force Accept All**

This method allows forcing acceptance of all purposes

**callback-based**

```swift
func setForceAcceptAll(completion: ACCompletionHandler?)
```

Example:

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.setForceAcceptAll { success in }
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
[self.appConsent setForceAcceptAllWithCompletion:^(BOOL success) {}];
```

{% endtab %}
{% endtabs %}

**modern concurrency**

```swift
func setForceAcceptAll() async -> Bool
```

{% tabs %}
{% tab title="Swift" %}

```swift
 let success = await appConsent.setForceAcceptAll()
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

### Force Deny All

This method allows forcing deny of all purposes

**callback-based**

```swift
func setForceDenyAll(completion: ACCompletionHandler?)
```

Example:

{% tabs %}
{% tab title="Swift" %}

```swift
appConsent.setForceDenyAll { success in }
```

{% endtab %}

{% tab title="Objective-C" %}

<pre class="language-objectivec"><code class="lang-objectivec"><strong>[self.appConsent setForceDenyAllWithCompletion:^(BOOL success) {}];
</strong></code></pre>

{% endtab %}
{% endtabs %}

**modern concurrency**

```swift
func setForceDenyAll() async -> Bool
```

{% tabs %}
{% tab title="Swift" %}

```swift
 let success = await appConsent.setForceDenyAll()
```

{% endtab %}
{% endtabs %}

### Confirm whether you need to request consent again for a specific floating purpose id

**callback-based**

```swift
func isFloatingNeedUpdate(id: String, completion: ACTypedResultVoidHandler?) 
```

Example:

{% tabs %}
{% tab title="Swift" %}

```swift
 appConsent.isFloatingNeedUpdate(id: "customFloatingPurpose") { result in }
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}

**modern concurrency**

```swift
func isFloatingNeedUpdate(id: String) async -> Bool
```

{% tabs %}
{% tab title="Swift" %}

```swift
let result = await appConsent.isFloatingNeedUpdate(id: "customFloatingPurpose")
```

{% endtab %}

{% tab title="Objective-C" %}
Not available
{% endtab %}
{% endtabs %}


# Logging

AppConsentUnified relies on `os_log` as recommended by Apple to log informational or debug messages. Our approach is to rely on Apple’s unified logging systems mechanisms, so the log level is configured by default at runtime.

### Overview <a href="#overview" id="overview"></a>

You can view log messages using the `Console` app, `log` command line tool, or XCode debug console.

AppConsentUnified debug messages are logged for subsystem `io.sfbx.appconsent` and category can be `AppConsentUnified`.

Below is a summary of some command examples using `log` that could be useful to setup logging preferences on your development environment.

* on your development Mac:
  * to check the current log config: `sudo log config --status --subsystem io.sfbx.appconsent`
  * to change the logging behavior: `sudo log config --mode "level:off" --subsystem io.sfbx.appconsent`
* in XCode simulator:
  * to check the current log config: `xcrun simctl spawn booted log config --status --subsystem io.sfbx.appconsent`

    to change the logging behavior: `xcrun simctl spawn booted log config --mode "level:off" --subsystem io.sfbx.appconsent`
  * the `--mode` option takes the following arguments (taken from `log` manual pages):

```
Modes can be specified as a comma-separated list of key:value pairs.

Valid keys and their values are:
level             off | default | info | debug
persist           off | default | info | debug
stream            live | default
```


# App Tracking Transparency

## App Tracking Transparency

App Tracking Transparency requires apps to get the user’s permission before tracking their data across apps or websites owned by other companies for advertising, or sharing their data with data brokers. Apps can prompt users for permission, and in Settings, users will be able to see which apps have requested permission to track so they can make changes to their choice at any time.

### Overview <a href="#overview" id="overview"></a>

Our latest frameworks are fully compatible and integrated with our Content Management Platform. On the App Consent back-office you must activate the `use iOS Att` switch, and activate the `disable success screen` switch (for devices below iOS 14).

{% hint style="info" %}
**INFO**

Don’t forget to register the NSUserTrackingUsageDescription key in your App Info.plist otherwise your app will crash.

If you did not update your application with the latest Framework and if your application runs on a device with iOS 14.5 or later, the IDFA will be 00000-00000-00000-00000.
{% endhint %}

#### [Setup a Floating Purpose for ATT](https://docs.sfbx.io/ios-api-reference/documentation/appconsent/apptrackingtransparency#Setup-a-Floating-Purpose-for-ATT) <a href="#setup-a-floating-purpose-for-att" id="setup-a-floating-purpose-for-att"></a>

On the App Consent back-office, you must create an Extra-Purpose for ATT feature.

Steps:

* Go to Extra-Purposes menu on the left, and click to Add Extra Purpose on the top-right corner.
* Enter a name, description and select Floating for Type.
* Save.
* Go to your Notice, click on Edit and on the configuration section, add your new Floating Extra Purpose for ATT.
* Save.

### Check if ATT is available for the user device <a href="#overview" id="overview"></a>

This method allows you to determine whether ATT is available on the device.

```swift
@objc public func appTrackingIsAvailable() -> Bool
```

### Verification of ATT Authorization Status within AppConsentUnified SDK <a href="#overview" id="overview"></a>

This  method allows you to verify whether ATT authorization have been given or not.

```swift
@objc func appTrackingAuthorizationGiven() -> ACATTAuthorizationGiven
```


# AppConsent SDK

Our iOS SDK supports the following targets:

* iOS, (iPhone, iPad and macOS through mac Catalyst) minimum deployment target 12.0
* tvOS, minimum deployment target 14.0 (tvOS support is still experimental as of AppConsent 4.4.0)

Our framework is built in Swift and UIKit, and is supported in Swift and Objective-C apps. It is possible to use it in a SwiftUI app following these instructions:

* [Adding UIKit views to SwiftUI view hierarchies](https://developer.apple.com/documentation/swiftui/uikit-integration).

{% hint style="info" %}
**NOTE**

The iOS AppConsent is now in version 4.X.X

Previous libraries AppConsentKit, AppConsentUIKit and AppConsentUIKitV3 are now obsolete, and won't be supported anymore in a near future.

If you still rely on any of the older frameworks, We advise you upgrade to AppConsent 4.X.X following these instructions:

* [Upgrade to AppConsent 4.X.X](https://docs.sfbx.io/ios-api-reference/documentation/appconsent/upgradetoappconsent/)
  {% endhint %}

## Add AppConsent to your xCode project

Our library is package as an XCFramework, it is available through Swift Package Manager, or CocoaPods, see this article about adding AppConsent to your XCode project:

* [Add AppConsent to your xCode project](https://docs.sfbx.io/ios-api-reference/documentation/appconsent/install/)

## How to use AppConsent

To get started integrating AppConsent into your app, read this article:

* [AppConsent Overview](https://docs.sfbx.io/ios-api-reference/documentation/appconsent/)

## API reference

All our iOS related articles and documentation are accessible through our full API reference [here](https://docs.sfbx.io/ios-api-reference/documentation/appconsent/).

## Screenshots

Screenshots for all supported languages and most supported screen sizes are visible [here](https://docs.sfbx.io/ios-screenshots/screenshots.html).


# React Native

Our CMP used to offer a choice of two UI styles: **classic** or **clear**.\
But with TCF2.2 the **classic** version is no longer maintained as it is not compliant with TCF2.2.

It is therefore necessary to use the **clear** module since Q4 2023!

[AppConsent Clear package on NPMjs.com](https://www.npmjs.com/package/appconsent-clear-reactnative)

{% hint style="danger" %}
**WARNING**

The Classic version is obsolete, please change to Clear module.

[AppConsent Classic package on NPMjs.com](https://www.npmjs.com/package/appconsent-reactnative)
{% endhint %}

***

## Getting started, samples, visual examples and API reference

Everything is explained [here](https://docs.sfbx.io/react-api-reference/).


# Flutter

There are three UI options for our CMP: **clear** or **classic**, both are used in the same way, only the design is different.

The **classic** UI is obsolete, for a new project, choose **clear**.\
It is therefore necessary to use the **clear** module since Q4 2023!

The third one **TV**, is suitable for tv applications, at the moment we only support android TV.

[appconsent-clear package on pub.dev](https://pub.dev/packages/appconsent_clear)

[appconsent-tv package on pub.dev](https://pub.dev/packages/appconsent_tv)

{% hint style="danger" %}
**WARNING**

The Classic version is deprecated, for a new install, choose Clear.

[appconsent-classic package on pub.dev](https://pub.dev/packages/appconsent_classic)
{% endhint %}

***

## API Documentation

API Documentation for our flutter bridges are included on the pub.dev page for each package. There is also an exemple implementation code included with each package to get you started.


# Unity

Our CMP is available for Unity as a Unity Package containing plugins for Android, iOS and tvOS. The package is available from the following git repository:

[AppConsent Unity package](https://gitlab.datalf.chat/customers/appconsent-unity.git)

Minimum supported version of unity is **2021.3.46f1 LTS**.

<https://unity.com/fr/releases/editor/archive>

***

## Simple execution

This example is taken directly from the sample code below.

<figure><img src="/files/iuuTcHpxnuA7ckpe0O1v" alt=""><figcaption></figcaption></figure>

### Runner Sample

Here's the Unity sample runner project into which we've integrated our solution. [Sample](https://gitlab.datalf.chat/customers/appconsent-runner-sample)

### Developer Sample

Here's a sample Unity project in which we've integrated our solution, enabling you to interact directly with it from the main screen. [Sample](https://gitlab.datalf.chat/customers/appconsent-unity-sample)

## Add the SFBX AppConsent package to your Unity project

From the *window* menu, open the *Package Manager*

<figure><img src="/files/8pL8rIU4KxMwVAyJm4lJ" alt=""><figcaption></figcaption></figure>

Inside the Package Manager window, click on the *+* button *+* in the top-left corner. From the drop-down menu select *Add package from git URL*.

<figure><img src="/files/D0LleEcTv2HUiXG0o9pv" alt=""><figcaption></figcaption></figure>

In the popup text field, enter the url to the package's git repository <https://gitlab.datalf.chat/customers/appconsent-unity.git> and click on *Add*.

<figure><img src="/files/6ZsjhuwU5Ml8R2nBaN1P" alt=""><figcaption></figcaption></figure>

The SFBX AppConsent package has been added to your project.

<figure><img src="/files/1Pd0FfFrRPkYXHld8Kok" alt=""><figcaption></figcaption></figure>

***

## Recommendation Android

The plugin allows you to define a success or error callback before CMP initialization, enabling you to log and/or use the CMP when it is fully initialized (asynchronous).

Here's an example of how CMP works on Android

```kotlin
using SFBX.AppConsent;
using UnityEngine;
using System.Collections;

public class AppConsentGUI : MonoBehaviour, ISDKCallbackListener, ISDKPresentNoticeListener
{
    ACNotice bridge = null;
    private bool isCmpInitialized = false;
    int centerX = Screen.width / 2;
    int buttonWidth = Screen.width / 2;
    int buttonHeight = Screen.height / 14;
    int buttonX;

    GUIStyle labelStyle = new GUIStyle();
    GUIStyle buttonStyle;

    string status = "";
    int logIndice = 0;

    void Awake()
    {
        AndroidJNIHelper.debug = true;
        // Only for Android - does not impact iOS !
        ACNotice.SetNoticeCallback(this);
        bridge = new ACNotice("18ecaea4-554a-4f74-9242-520fe62058a8");
        UpdateStatus("bridge instanciated => " + bridge);
    }

    // Start is called before the first frame update
    void Start()
    {
        labelStyle.alignment = TextAnchor.MiddleCenter;
        labelStyle.normal.textColor = Color.white;
        buttonX = centerX - ( buttonWidth / 2);
        
        bridge.InitACNotice();

        #if UNITY_IOS || UNITY_TVOS
            // On iOS, there's no need for a callback, so you can directly change the boolean's state. 
            OnReadyOnSuccess();
        #endif
    }

    // Update is called once per frame
    void Update()
    {
        centerX = Screen.width / 2;
        buttonWidth = Screen.width / 2;
        buttonHeight = Screen.height / 14;
        buttonX = centerX - ( buttonWidth / 2);
    }

    void OnGUI()
    {
        labelStyle.fontSize = 28;
        labelStyle.wordWrap = true;

        buttonStyle = new GUIStyle(GUI.skin.button);
        buttonStyle.fontSize = 28;

        GUI.Label(new Rect(buttonX, buttonHeight * 2, buttonWidth, buttonHeight), "SFBX AppConsent", labelStyle);

        if (GUI.Button(new Rect(buttonX, buttonHeight * 4, buttonWidth, buttonHeight), "Display CMP", buttonStyle))
        {
            if(isCmpInitialized == true && bridge != null){
                bridge.SetPresentNoticeListener(this);
                bool isDisplayed = bridge.ShowNotice();
                if(isDisplayed == true){
                    UpdateStatus("Notice displayed");
                }else{
                    UpdateStatus("Notice not displayed, look at your consent or RGPD country.");
                }
            }else{
                UpdateStatus("AppConsent not ready yet");
            }
        }

        if (GUI.Button(new Rect(buttonX, buttonHeight * 6, buttonWidth, buttonHeight), "Settings", buttonStyle))
        {
            if(isCmpInitialized == true && bridge != null){
                bridge.ShowSettings();
                UpdateStatus("Settings displayed");
            }else{
                UpdateStatus("AppConsent not ready yet");
            }
        }

        if (GUI.Button(new Rect(buttonX, buttonHeight * 8, buttonWidth, buttonHeight), "Check Consents", buttonStyle))
        {
            if(isCmpInitialized == true && bridge != null){
                bool consent = bridge.ConsentGiven();
                bool allConsentables = bridge.AllConsentablesAllowed();
                bool gdpr = bridge.IsSubjectToGDPR();
                bool acceptAll = bridge.UserAcceptAll();
                bool extra = bridge.ExtraVendorAllowed("TobuM9Iw");
                bool consentable = bridge.ConsentableAllowed(1,0);
                UpdateStatus("consents: " + consent + allConsentables + gdpr + acceptAll + extra + consentable);
            }else{
                UpdateStatus("AppConsent not ready yet");
            }
        }

        if (GUI.Button(new Rect(buttonX, buttonHeight * 10, buttonWidth, buttonHeight), "Reset Consents", buttonStyle))
        {
            if(isCmpInitialized == true && bridge != null){
                bridge.ClearConsents();
                UpdateStatus("Cleared consents");
            }else{
                UpdateStatus("AppConsent not ready yet");
            }
        }

        GUI.Label(new Rect(0, buttonHeight * 12, Screen.width, buttonHeight), status, labelStyle);
    }

    public void OnReadyOnSuccess()
    {   
        UpdateStatus("Appconsent onReadyOnSuccess");
        isCmpInitialized = true;
        
        Debug.Log("AppConsent is ready to be use !");
        UpdateStatus("Setting listener + trying to show notice");
        bridge.SetPresentNoticeListener(this);

        bool isDisplayed = bridge.ShowNotice();
        if(isDisplayed == true){
            UpdateStatus("Notice displayed");
        }else{
            UpdateStatus("Notice not displayed, look at your consent or RGPD country.");
        }
    }
    
    public void OnReadyOnError(AndroidJavaObject err)
    {
        isCmpInitialized = false;
        Debug.LogError("Failed to start AppConsent");
    }

    public void OnConsentGiven()
    {
        Debug.Log("The user has given his consent");
        UpdateStatus("Consent given !");
    }

    public void OnConsentGivenError(AndroidJavaObject err)
    {
        Debug.LogError("An error as occurred, please read Log.");
        UpdateStatus("An error as occurred, please read Log.");
    }

    private void UpdateStatus(string log){
        if(logIndice % 10 == 0){
            status = logIndice++ + " - " + log;
        }else{
            status = status + "\n" + logIndice++ + " - " + log;
        }
    }
}
```

## API Documentation

Detailed API documentation is available [here](https://docs.sfbx.io/unity-api-reference/index.html).

## FAQ

All our FAQs can be found in our git [README](https://gitlab.datalf.chat/customers/appconsent-unity/-/blob/master/README.md)


# Find trackers on your website (Consent Guard)

Consent Guard is useful to see the trackers that are placed on your website, engage a clean-up by removing unwanted cookies if necessary, and finally create your source accordingly.

1. In the left menu, go to **Consent Guard**.&#x20;
2. Then enter a URL address you would scan in the field **Find out which trackers are used on your website.**
3. &#x20;**Enter your website URL and run a scan.**

<figure><img src="/files/DLz3NrXOIpED1tDoS9Tb" alt=""><figcaption></figcaption></figure>

Our tool will need a few seconds to find all the trackers present on your website.

Then, on the result page, you have these informations for each tracker :

* a name
* a type: cookie; local storage; storage
* a domain ( it's an URL)
* a vendor name
* the privacy policy link of the vendor
* a tracker's category ( necessary, functional, analytics, performance, advertising, other)
* a description
* an expiration date

<figure><img src="/files/5iDRW07mRRMYBnNkzbPW" alt=""><figcaption></figcaption></figure>

Once a report is created, you can access it from the main Consent Guard page, at the Report section. From there you can export any report in PDF or CSV format and also delete some reports from the column Actions.

Now, you know all your vendors you can [create your source](/configuration/step-2-create-a-notice/2.1.-create-a-source) and add them accordingly in the vendor's field.


# Statistics and A/B test

{% content-ref url="/pages/MCbN8VqxtERBJNDMZEW3" %}
[Understand my dashboard](/go-further/statistics-and-a-b-test/understand-my-dashboard)
{% endcontent-ref %}

{% content-ref url="/pages/Vj6qYqNPZhji4g3Knuwz" %}
[Compare multiple notices (A/B tests)](/go-further/statistics-and-a-b-test/compare-multiple-notices-a-b-tests)
{% endcontent-ref %}


# Understand my dashboard

This page explains all the key performance indicators you can find in our dashboard.

***

## Real Time Computing

All the KPIs and metrics exposed in the dashboard are computed in real time. Every time you refresh your page, you get new metrics or consent rate.

<figure><img src="/files/0hgwk5pXhxYHX16e2lr2" alt=""><figcaption></figcaption></figure>

### Key Statistics

#### Consent-In

This is the number of positive consents on all the purposes of a notice. By default over the last hour.

#### **Consent-Out**

This is the number of negative consents on Graall the purposes of the consent notice. By default over the last hour.

#### Consent Rate

This is the rate of Consent-In on \[consent in + consent out + consent mixed]. By default over the last hour.

#### Bounce Rate

This is the rate of internet/mobile users who see the consent notice but do not give consent. This metric is calculated over a rolling hour.

#### Consent Mixed

A mixed IN/OUT signal is raised when a user has a combination of switches that are both positives and negatives. Example: True to Store and/or access information on a device and False on Select basic ads.

***

## Graphic

<figure><img src="/files/UM0KutDp0q81dbd0HZq7" alt=""><figcaption></figcaption></figure>

#### Time Range

Using the selector on the top right corner of the dashboard, you can control the time range of the graph.

<div align="left"><figure><img src="/files/e0NTXl4BkDOv5nTcNfhc" alt="" width="310"><figcaption></figcaption></figure></div>


# Compare multiple notices (A/B tests)

You can run A/B tests in order to compare consent rates between various notices. This section will help you to set up your A/B test.

***

## Introduction

Using AppConsent, you can modify and update your notice settings. But you may want to compare the consent rate of differently defined notices to implement the notice generating the most consent-in.

AppConsent allows you to lunch A/B tests campaigns in order to test and compare performance of different notice configurations (with or without banner, order of buttons...).

You can create an A/B test with 2 or more records running on the same time, on the same source. This various notices are called "Variations". Each variation will be exposed alternatively to users, according to your parameters.

An A/B test can be implemented at anytime on a source.

While an A/B test is running, you can consult the consent rate of each variation through the dashboard. But you can't modify the settings of the notice variations.

You can stop the A/B test whenever you want. At this moment, you will have to choose one of the notice variations. The one you choose will be implemented definitively on the source, and your A/B test stops. After that, you could modify the parameters of this notice if needed, or start an other A/B test.

You have subscribed to AppConsent Standard, you can run 2 A/B test campaigns simultaneously.

You have subscribed to AppConsent Premium, you can run as many A/B test campaigns as you want, simultaneously.

***

## Prerequisites before creating an A/B test

Before creating an A/B test, you need to have a notice declared in AppConsent.

* This can be a notice running since a while on your website. In that case you don't have to create a new notice and you can read the paragraph **Create an A/B test** below. The A/B test will run instead of your current notice.
* Or it can be a new notice that you will implement when you will start your A/B test (for example if you are a new client and want to start by an A/B test). In that case, you need to create a notice first. Read this page to learn how to create a notice.

***

## Set up an A/B test

### 1. Create an A/B test

To create an A/B test, click on **A/B test** in the left menu.

<figure><img src="/files/KoLNYlDt6ID0kCNujyi8" alt=""><figcaption></figcaption></figure>

Then click on **Add A/B test** on the left top.

<figure><img src="/files/VQnaydKEfjPbkZENDWzw" alt=""><figcaption></figcaption></figure>

In the list of sources that appears, select the one on which you want to apply the A/B test. It can be whether a web source or a mobile app source.

<figure><img src="/files/MthgsRvPCbZ0FLSRUfyM" alt=""><figcaption></figcaption></figure>

Then select the Notice Version ID you want to use for the A/B test. We call this notice, the **parent version**. Then click on **Create A/B test**. A copy of this notice will be created in the A/B test.

<figure><img src="/files/K7ijxxdsZvRhSqlJgiNV" alt=""><figcaption></figcaption></figure>

Your A/B test has now an "Open" status. Meaning that it is created, but not yet activated on your website or app.

* ID: A/B test ID
* Source: Source where the A/B test will be implemented
* Parent version ID: ID of the notice from the one the A/B test has been created (the parent notice).
* Note: you can add note by clicking one the 3 dots at the right. This note will not appear in the A/B test on the website, it's just for you own usage (for example to distinguish 2 variations you could add this notes: Notice with banner / notice without banner).
* Status:
  * Open: the A/B test is created, but not yet activated on your website or app
  * Running: the A/B test is live on your website or app
  * Done: the A/B test has been stopped
* Update date: date of the last update
* Test start date: date when you start the A/B test
* Test end date: date when you stop the A/B test
* 3 dots at the right: if you click on this dots, you can edit the note field or delete the A/B test.

To see the details of the A/B test, click on the line of the A/B test.

<figure><img src="/files/VGZYLOMjxfdjdzFrAc6n" alt=""><figcaption></figcaption></figure>

The details of the A/B test are displayed:

* App Key : Notice App Key
* Start A/B test: click on this button when your A/B test is ready.
* Add variation: click on this button to add a variation.
* Line with a test number 1: this is the first variation of this A/B test, automaticaly created from the parent notice.

### 2. Add notice variations for the A/B test

{% hint style="info" %}
**INFO**

To be run, an A/B test needs at least 2 variations. We call **Variation** a notice with some settings. Each variation can have different settings.&#x20;

For example:&#x20;

* Variation 1: notice with banner, UI in a single step, CTA accept/Configure in the banner, CTAs enabled in notice.&#x20;
* Variation 2: notice without banner, UI in several steps, no CTA in the notice.
  {% endhint %}

There are 2 ways to create a new variation:

* Click on **Add variation**
* Or create a duplicate from the first variation.

<figure><img src="/files/e9Rr8zfVlZg9DFsMIFKF" alt=""><figcaption></figcaption></figure>

#### **2.1. Add a variation through the "Add variation" button**

Click on **Add variation.** The list of existing notice from the same parent notice appears.

You can select any notices as long as it is not part of an experiment and is from the same source as the basic notice of your experiment.

<figure><img src="/files/M8H8nzQXO1aPP2YZOH5E" alt=""><figcaption></figcaption></figure>

Select one or several notice(s) and click on **Apply**.

<figure><img src="/files/Mc0rZ04vyKk50ff6QCPj" alt=""><figcaption></figcaption></figure>

A new variation is created.

#### **2.2. Add a variation through the "Duplicate" button**

Click on the 3 dots at the end of the line of the variation you want to copy, and click on **Duplicate**.

<figure><img src="/files/Gr7C4c3JqSANQnx2yFzk" alt=""><figcaption></figcaption></figure>

A new variation is created.

{% hint style="info" %}
**INFO**&#x20;

You can create as many variations as you want, but we recommend not to create too many. The larger the number of variations, the lower their weight, and the less frequently appear on your site.
{% endhint %}

### 3. Edit and configure a variation

Each variation can be edited and configured with its own settings.

Click on the 3 dots at the end of the line of the first variation and click on **Edit**.

<figure><img src="/files/zyengl6fL82PLgwH5We8" alt=""><figcaption></figcaption></figure>

The notice form appears, with the same configuration fields than a notice. You can modify all settings.

<figure><img src="/files/h9qeavg3bpS3LWmujYdV" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/2OxAFj0XoIefdsGwQmaM" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/wkJBYLOlaWu18MFgOmhr" alt=""><figcaption></figcaption></figure>

The field "Weight" is specific to the variation form. You need to fill in this field with a number corresponding to a percentage. This percentage will define the display frequency of the variation, compared to other variations of the A/B test.

For example you can have a weight of 50/50 or 30/70 or 30/20/50, or 25/25/25/25, etc.

#### **3.1. Preview or delete a variation**

2 other actions are enabled for each variation:

* You can preview your settings by clicking on **Preview**
* You can delete a variation by clicking on **Delete**

<figure><img src="/files/FF6MBD0huvhQ27bTN9CM" alt=""><figcaption></figcaption></figure>

#### **3.2. Organize your variations**

If you want to easily identify the variations in the variations table, you can modify the **Note** field in the editing form. This text will not appear in the notice on your website or app.

<figure><img src="/files/c3nKdcXJD2GZE5ZsOK8W" alt=""><figcaption></figcaption></figure>

***

## Start an A/B test

When all your variations are ready to test, you can start the A/B test.

{% hint style="warning" %}
**CAUTION**

Make sure all your settings are well defined before clicking on **Start A/B test**, as you could no more modify them when the A/B test will be running.
{% endhint %}

Click on **Start A/B test**

{% hint style="warning" %}
**CAUTION**

If you haven't fill the field **Weight** for each variation, or if the total weight is not 100%, the A/B test cannot start.
{% endhint %}

{% hint style="warning" %}
**CAUTION**

There can't be 2 A/B tests running at the same time on the same source. If another A/B test is already running on the source, you need to stop it before starting a new one.
{% endhint %}

Few seconds after clicking on **Start A/B test**, the A/B test will run, and its status is **Running**, with a **Test start date.**

At this stage, the A/B test can no more be modified. You can only view details (throught the variation form), but settings cannot be updated. You can still see a preview of each variation, by clicking on **Preview**.

While an A/B test is running, the parent notice cannot be modified or deleted.

{% hint style="info" %}
**INFO**

While the A/B test is running, you can check the statistics for each variation. To do so, read the dedicated section.
{% endhint %}

***

## Stop the A/B test

You can run an A/B test as long as you want and stop it whenever you want. When you consider your test is done, click on **Stop A/B test**.

<figure><img src="/files/nGzyWLbDsDcqx0nXIstz" alt=""><figcaption></figcaption></figure>

The *Stop A/B test*\* button changes to **Confirm stop**. You need to pick one of the variations before confirming. The variation you choose is the one that will be permanently implemented on your website or Mobile application.

<figure><img src="/files/IBZNcyPqupnS2ReZlcS1" alt=""><figcaption></figcaption></figure>

Once you have picked one of the variations, click on **Confirm stop**. The A/B test stops immediately (its status is **Done** and a **Test end date** is filled in) and the notice you have picked is now running on your website or application.


# Compliance

{% content-ref url="/pages/k02ugo1IvjZ9k6wQxWMx" %}
[Get proof of your consents (Proof)](/go-further/compliance/get-proof-of-your-consents-proof)
{% endcontent-ref %}

{% content-ref url="/pages/1JLZzLBN3zr3yNarJgiz" %}
[Display privacy widget](/go-further/compliance/display-privacy-widget)
{% endcontent-ref %}


# Get proof of your consents (Proof)

***

## Get proof of your consents (Proof)

From your AppConsent account access the entire history of consents and be able to provide proof of collection at any time.

### Get your consents history

Click on the **Proof** tab.

<figure><img src="/files/qBRFWsl7uJ5uFwSmZTQT" alt=""><figcaption></figcaption></figure>

* **Source:** Select the source you want
* **Notice:** Then choose the notice you want by selecting the right Notice ID.
* **UUID:** For Universally Unique Identifier, these IDs are the ones of Appconsent users . These IDs are generated differently depending on the user's platform.

{% hint style="info" %}
**NOTE**

We talked here about UUID-V4. These are randomly generated strings with a very low probability of collision.

On the Web, it is the collector that generates a UUID, it is stored in local storage or in a cookie and is quite volatile.

On Android, the UUID is created by the client from the AAID (Android Advertising ID) of the user. This AAID is a UUID specific to the user's device and is quite robust.

On IOS it's like on Android but the phone ID is called MAID (Mobile Advertising ID).
{% endhint %}

## How to retrieve UUID?

### For the Web

#### Solution 1

Go in your browser and open the console. ( or Developers tools , it depends of your browser brand )

Past this piece of code `JSON.parse(localStorage.getItem('appconsent')).consents.uuid` and hit Enter. It will give you directly your UUID. ( works on any web browser ).

​![](/files/bRM2fh19VedIey0Pwp1i)

#### Solution 2

To retrieve your UUID you can go to the website where your Notice run and then open your Browser Console or the Firefox developer tools. You should have this:

<figure><img src="/files/HQ85tup6VBemQ6m2uPo6" alt=""><figcaption></figcaption></figure>

Then go to **Application** and click on **local storage**, like this:

<figure><img src="/files/1VkyL1Iqssrjfx5kUYCm" alt=""><figcaption></figcaption></figure>

In the exemples below, 127.0.0.1:8000 must match your website domain: like mywebsite.com and so on.

Open **consents** and you should see your uuid like the example below:

<figure><img src="/files/gBkz3SBUnvQBmlU38Iln" alt=""><figcaption></figcaption></figure>

Now that you have your UUID you can insert it in the UUID field.

{% hint style="info" %}
**NOTE**

For Android and iOS we talk about **IDFA** or **GAID. It's not the same process.**&#x20;
{% endhint %}

## For Android

Technical solution via our SDK

Our SDK provides a method for accessing this information.

**getUserId** : <https://docs.sfbx.io/android-api-reference/latest/appconsent-ui-v3-premium/appconsent-ui-v3/com.sfbx.appconsentv3/-app-consent/get-user-id.html>

If you want to read the information in the logs, use this snippet:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
private fun logAndroidAdvertisingID(){
    if(BuildConfig.DEBUG){
        Log.d("AAID", "Android Advertising ID: ${appConsent.getUserId()}")
        Log.d("AAID", "isLimitedTrackingEnabled: ${appConsent.isLimitedTrackingEnabled()}")
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
private void logAndroidAdvertisingID(){
    if(BuildConfig.DEBUG){
        Log.d("AAID", "Android Advertising ID: " + appConsent.getUserId());
        Log.d("AAID", "isLimitedTrackingEnabled: " + appConsent.isLimitedTrackingEnabled());
    }
}
```

{% endtab %}
{% endtabs %}

Why use this method? Because the AAID on your device may not be the same. If, for example, you've restricted AAID tracking in the settings or simply deactivated the AAID, the SFBX SDK generates its own UUID V4.

You'll get this in the logs:

<figure><img src="/files/3evbHhtoQYnA9LnX6NPs" alt=""><figcaption></figcaption></figure>

#### No-code solution (recommended version)

Here the technique is rather simple: access your application's default SharedPreferences file directly via Android Studio's Device Explorer.

Open the file in the directory:

> data/data/**\<your\_app\_package\_name>**/shared\_prefs/**\<your\_app\_package\_name>**\_preferences.xml

And find the following keys:

* <pre class="language-xml" data-overflow="wrap"><code class="lang-xml">&#x3C;string name="appconsent_user_id">babce8b9-f686-4ec9-8009-c8d44d04ce52&#x3C;/string>
  </code></pre>
* ```xml
  <string name="appconsent_user_id_tracking_limited">false</string>
  ```

## How to retrieve AppConsent Key

{% hint style="info" %}
**NOTE**

If you know your notice versionID, you can bypass this step and jump to **Result**&#x20;
{% endhint %}

You need to open your browser, open the development tools , go to **Application**, then **Local Storage** then find the name of the website and locate an AppConsent key.

<figure><img src="/files/HcHcZHuJ3AeQAzwsf4Tw" alt=""><figcaption></figcaption></figure>

## Result

Now that all the informations are stored you can click on search and here it goes your history:

<figure><img src="/files/begBymGlZADQ3DY4b5Ah" alt=""><figcaption></figcaption></figure>

At the left you have the date and hours of the consent and the icon indicate if it was accept all / refuse all or the user's settings.

Also, you can find the Consent string that you can copy and paste and all the **purposes, special feature and extra-purposes** accepted.


# Display privacy widget

You will find below all the stuff to have a floating privacy center or to micro-banner to put in the footer.

***

## Reminder of Legislation

According to GDPR, it is mandatory to give the user the option to modify their choices at any time and in a simple way. You have two options:

* Provide access to the CMP with a link in the footer or menu
* Provide access to the CMP with our "Privacy Widget"

## Privacy widget

The Privacy Widget AppConsent is a button that appears on your web page and allows the user to quickly interact with the CMP to manage their consents.

This is a simple and elegant way to allow your users to manage their consents.

<div align="left"><figure><img src="/files/SdEQ6qxBpvCkaQ82O49j" alt="" width="167"><figcaption></figcaption></figure></div>

## Implementation

### Implementation using the widget

You can configure the Privacy Widget in the notice configuration form, see Configurer une notice web.

For an Internet user to be able to see it, consent must already have been given.

It exists in two colors, `dark` and `clear`.

<div align="left"><figure><img src="/files/MhOwoRM66e8zPBzh3c7F" alt="" width="167"><figcaption></figcaption></figure> <figure><img src="/files/SdEQ6qxBpvCkaQ82O49j" alt="" width="167"><figcaption></figcaption></figure></div>

You can also place the widget either :

* In the bottom left-hand corner
* In the bottom right-hand corner

### How to add the privacy widget

1. Go to the backoffice in the Notices menu.&#x20;
2. Click on the notice you want to configure
3. Go to Banner layout tab&#x20;
4. Switch on `Display privacy widget`&#x20;

<figure><img src="/files/pOyKGbV9wAXiM7sahe0N" alt=""><figcaption></figcaption></figure>

#### Configuring style of the widget

By default, the widget is on `clear` style, but you can also configure the `dark` style. For this, click on the style you want to show on your website.&#x20;

<figure><img src="/files/SX39KT00ipJcLrN1wesO" alt=""><figcaption></figcaption></figure>

#### Configure the widget position

By default the widget position is on `bottom right` of the screen, but you can also configure the widget on `bottom left`. To configure, click on the option you want to show on your website.

<figure><img src="/files/SBwwalyiqGPfDw8BA24E" alt=""><figcaption></figcaption></figure>

#### Edit the widget label

To edit the label in the text field :

<figure><img src="/files/lFyTUZ43Wy5r9IEdLebY" alt=""><figcaption></figcaption></figure>

#### **Advanced option :** A**dd the widget to a specific part of your website**

For the moment, the configuration interface does not allow this mode of operation. You need to use the configuration variable `configSFBXAppConsent`, like this:

```markdown
<script type="text/javascript">
const configSFBXAppConsent = {
  appKey: 'PUT YOUR APPKEY',
  privacyWidget: {
    target: document.getElementById('target'),
    color: 'dark',
    text: 'Privacy center',
  }
}
</script>
```

### **Link implementation**

You can add the link to your website, for example :

```html
<a href="#" onclick="javascript:__tcfapi('show', 2, function(){}, {jumpAt: 'privacy'})">Display the Privacy center</a>
```

If your website is created with aspx C# and .NET framework please use this :

```html
<a href="#" OnClick="__tcfapi('show', 2, function(){}, {jumpAt: 'privacy'})">Display the Privacy center</a>
```


# Advanced settings notice

{% content-ref url="/pages/CuS2EXbu79utMOBvX4WG" %}
[Notice history (Rollback)](/go-further/advanced-settings-notice/notice-history-rollback)
{% endcontent-ref %}

{% content-ref url="/pages/9Vew9c7KljjVxBZiSEo1" %}
[Display the notice to a defined group of users (Cohorts)](/go-further/advanced-settings-notice/display-the-notice-to-a-defined-group-of-users-cohorts)
{% endcontent-ref %}

{% content-ref url="/pages/nNcdrnZrtjr5iD9haxoi" %}
[Team collaboration on 1 notice (Copy from linked account)](/go-further/advanced-settings-notice/team-collaboration-on-1-notice-copy-from-linked-account)
{% endcontent-ref %}

{% content-ref url="/pages/MT0wCVd8CMZ89WMFJ0U2" %}
[Specific consents (Floating purposes)](/go-further/advanced-settings-notice/specific-consents-floating-purposes)
{% endcontent-ref %}


# Notice history (Rollback)

Only for iOS and Android mobile notices

***

From **Notices**, choose a notice, go to the **Rollback** section

<figure><img src="/files/4AmBorcPPyBWKNlFSKVN" alt=""><figcaption></figcaption></figure>

A window allows you to return to a previous version of a notice.

<figure><img src="/files/osyCEyf4aTJp4NW0fDMV" alt=""><figcaption></figcaption></figure>


# Display the notice to a defined group of users (Cohorts)

It is possible to display a specific consent window to a group of people defined by knowing their identifiers. From the side menu, click on **Cohorts** and then **Add cohort**.

<figure><img src="/files/t33gHCBk0IXN5efZzgYw" alt=""><figcaption></figcaption></figure>

Search for your file in CSV format, the values must be separated by semicolons: ";". Description of the CSV file :

* 3 lines
* Line 1: a header that describes what each of the 3 columns corresponds to
* Line 2 and 3: the members of a cohort
* Column 1 UUID: Appconsent identifier of a user
* Column 2 external\_id (name defined by you): identifier of a user external to Appconsent
* Column 3 popname: name of the cohort

```
uuid;external_id_name;popname
9429e300-e33d-4590-83e8-e19bf611c1d6;external_id_1;population_name
7f03d94b-b8a1-4701-9b48-4e25a49f9645;external_id_2;population_name
```

You will then have to fill in the source Appkey (of the notice that is displayed by default on a web or app source) and fill in the destination Appkey (of the notice for the cohort). At any time you can stop a cohort from the Cohorts menu, and by clicking on the drop-down menu at the end of each line.


# Team collaboration on 1 notice (Copy from linked account)

{% hint style="info" %}
**INFO**

If you need it, [contact us](mailto:support@sfbx.io) so we can link several of your accounts together.
{% endhint %}

## How Copy from linked account work

<figure><img src="/files/x9vBYxNE1XAcqPWHBlte" alt=""><figcaption></figcaption></figure>

By choosing to link two accounts, you will have one parent account and one child account.

In the example below, our Parent account is **Demo Premium**

<figure><img src="/files/U42VrJizscoqMRuAaBE4" alt=""><figcaption></figcaption></figure>

The **Chandago Test Premium** is the child account

<figure><img src="/files/fUm1HXqAJYbKPbbGuVzg" alt=""><figcaption></figcaption></figure>

Now if you want to export a Notice from the **child account**, you have to go to "Notices" of the **parent account**. and then click to **COPY FROM LINKED ACCOUNT**

<figure><img src="/files/jZTVckgFKgMB6pBLHlV0" alt=""><figcaption></figcaption></figure>

* Source account : child account
* Source notice : select the **Version ID** of the notice who will replace the destination notice
* Destination notice : select the **Version ID** that is going to be **crush**.

{% hint style="danger" %}
**CAUTION**

By doing this you will delete the `Destination notice` of your back-office
{% endhint %}


# Specific consents (Floating purposes)

## Floating purpose feature

The Floating Purpose feature allows you to save consent in our blockchain for a non-IAB purpose that you have created, and whose acceptance or refusal is managed by you outside of our CMP (e.g., acceptance of the General Terms of Use). This way, you keep an immutable record of your user's choice in our blockchain.

## Creating a floating purpose

Either from the source creation page via the "create a non-IAB purpose" button or from the "Non-IAB Purposes" tab via the "Add Non-IAB Purpose" button.

<figure><img src="/files/FrmYQ8FRNd4UQ8Zd3bdS" alt=""><figcaption></figcaption></figure>

Then choose the "Floating" type from the drop-down menu and select the languages in which you want to provide information about this purpose.

<figure><img src="/files/U6Ukg8UoII5Ckj85o2Ii" alt=""><figcaption></figcaption></figure>

Then, for each selected language, provide the name and a description of this floating purpose (this information will not be visible to your customers).

## Adding a floating purpose to your CMP

To add a floating purpose that you have created to your notice, go to step 2 "Configuration" of the notice creation and select it from the "Floating Purpose" drop-down menu. Finally, click "Next" or "Save", and your floating purpose will be added.


# Support

***

Support is available in French and English.

| XChange Plan                                                                                                                                                              | Standard/Essential Plan                                                                                                                          | Enterprise Plan                                                                                                                                                                                                                                   |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p></p><p>Help Center <a href="https://docs.sfbx.io/​ "><https://docs.sfbx.io/​> </a>-</p><p>Email support 5 days a week and guaranteed response within 2 days</p><p></p> | Our teams answer you by mail from Monday to Friday at <support-cmp@sfbx.io>​ - Email support 5 days a week and guaranteed response within 2 days | <p></p><p>Our teams answer you by mail from Monday to Friday at <a href="mailto:support-cmp@sfbx.io"><support-cmp@sfbx.io></a> + Visios + dedicated Slack channel (invitations received by email) - Guaranteed response within 4 hours</p><p></p> |

For any GCM (Google Consent Mode) question or issue, please use our direct support path through : <support-cmp-gcm@sfbx.io>​. If needed you will be escalated to an account executive.

The entire company is ready to help you put together the best solution for your domains.

In the meantime, do not hesitate to read our [FAQ](/help/faq), the answer to your questions may already be there.


# Glossary

***

## Keywords of our sector

### CMP

For Consent Management Platform. The CMP is a dedicated technological platform specifically to the collection, recording, return and proof of consents given by Internet users / mobile users in the field of personal data, on the various digital platforms (websites, applications, TV connected, etc). It also ensures the transmission of consent parameters from end users to all partners wishing to use this collected data and for which the authorization request has been submitted.

### Consent

Designates any manifestation of will, free, specific, enlightened and unambiguous by which the person concerned accepts, by a declaration or a clear positive act, that personal data concerning them are processed, as defined by the Personal Data Regulations. The latter must be obtained before deposit and reading of cookies or tracers on the digital platform used by the Internet user / mobile user. Consent is not a new concept, since it was already included in the Data Protection Act then in the ePrivacy directive. However, theGDPR completes its definition and clarifies this concept on certain aspects, in order to allow data subjects to exercise real and effective control over the processing of their data. Consent is one of the 6 legal bases provided for by the GDPR authorizing the implementation of personal data processing with legal obligation, contract, the public interest mission, the protection of vital interests and the legitimate interest.

### Organic data

Data collected on the user's device for which there is no need for system permission. This data is basic and non-intrusive. Examples: manufacturer, OS, version, etc...

### Personal data

It is any information relating to a natural person likely to be identified or identifiable, directly (example: surname or first name) or indirectly (example: a number identifier, biometric data, voice or image). Identification of a person can be produced from a single data (for example: a name, a fingerprint, a postal address, email address, telephone number, social security number, etc) or from a crossing of a set of data (example: a man having such profession, living at such address and born on such day).

### Notice

This is the name we give to the consent collection window that is displayed on all digital platforms (websites, applications, connected TV, etc.), collecting personal data. This window informs and requests consent to file cookies / trackers on the user's computer / phone / TV (etc.). Via this notice, the user is able to make his choices in an informed way by knowing the whole the partners and the purposes for which these cookies or tracers are placed. This window must meet the requirements of the GDPR, the ePrivacy directive as well as theCNIL guidelines and recommendations. For example: the presence of the “All accept ”,“ Refuse all ”and“ Personalize my choices ”.

### Publishers

Publishers provide capacity and inventory, in their applications or websites, which allow advertisers to serve advertisements. They are the ones who must directly obtain the consent of their visitors. In the IAB Framework, publishers are digital media that publishInternet content or mobile applications. The editors represent the first part, that is, the website or application that the user has sought to access.

### Tracers

what is more generally known as a cookie, i.e. the reading and/or writing of information on a user terminal, whether on a computer's browser, a smartphone, a voice assistant, a connected TV, or any other connected object.

The tracers are for example :

* HTTP cookies,
* flash cookies,
* the result of the calculation of a unique fingerprint of the terminal in the case of fingerprinting (calculation of a unique identifier of the terminal based on elements of its configuration for tracing purposes),
* invisible pixels or web bugs,
* any other identifier generated by a software or operating system (serial number, MAC address, Unique Terminal Identifier (UTI)), or any data set that is used to calculate a unique terminal fingerprint (e.g. via a "fingerprinting" method).

***

## On the regulator side

### CNIL

The "Commission Nationale de l'Informatique et des Libertés"(CNIL) was created by the French Data Protection Act of January 6, 1978. It is responsible for ensuring the protection of personal data contained in computer files and processing or paper, both public and private. Thus, it is responsible for ensuring that information technology is at the service of the citizen and that it does not infringe on human identity, human rights, privacy, or individual or public freedoms. The CNIL is an independent administrative authority (AAI), i.e. a public body that acts on behalf of the State, without being placed under the authority of the government or a minister. It is composed of 18 elected or appointed members and is supported by services. Its role is to warn, advise and inform the public at large, but it also has the power to control and sanction.

### ePrivacy Directive

European Directive of 12 July 2002 on the protection of privacy in the electronic communications (2002/58). This European directive aims to specifically protect life private on the Internet. It was transposed and integrated into the Data Protection Act in 2004.

### Loi Informatiques et Libertés

Created in 1978, modified in 2004 then in 2019 to integrate the ePrivacy directive then theGDPR. It regulates all the processing of personal data. It applies therefore to all sectors that use personal data as part of their activities. Several provisions are included in this law, namely:

* The obligation to declare files containing personal data to the CNIL,
* The prohibition of collect sensitive data, i.e. relating to religion, health,policy, etc. (with exceptions),
* The principle of fair data collection,
* The obligation ensure the security of all data collected,
* The obligation to inform individuals concerned with the collection of their data,
* The right to access, modify and deletion of the data in question

### GDPR

The acronym GDPR stands for " General Data Protection Regulation ". The GDPR regulates the processing of personal data on the territory of the European Union, since May 2018. The context legal adapts to follow the evolutions of technologies and our societies (uses increased digital, development of online commerce, etc.). This new regulation European law is a continuation of the French Data Protection Act of 1978 and strengthens the control by European citizens of the use that can be made of data concerning them. It harmonizes the rules in Europe by providing a legal framework unique to professionals. It allows them to develop their digital activities within the EU based on the trust of users. Any organization, whatever its size, its country of establishment and its activity may be concerned. Indeed, the GDPR applies to any organization, public and private, which processes personal data on its behalf or not, therefore:

* that it is established on the territory of the European Union,
* or that its activity directly targets European residents

***

## On the market side

#### AMP \[Standard / Premium]

### For Accelerated Mobile Pages, is a publishing format created by Google to accelerate the display of pages on mobile devices.

### ATT \[Standard / Premium]

On iOS, user consent for ad tracking is managed by the AppTrackingTransparency (ATT) system. App developers will now be required to use the AppTrackingTransparency framework if their app collects user data and shares it with third parties for tracking purposes between apps and websites. If the user does not actively accept ATT, IDFA will not be available and app tracking across websites and apps will be prohibited.

### MAU / UU

For Monthly Active Users / Unique users. This is the monthly number of active users.

### SLA

For Service Level Agreement, is a contract or part of a contract by which an IT provider undertakes to provide a set of services to one or more clients. In other words, it is a contractual clause that defines the precise objectives and the level of service that a customer is entitled to expect from the signatory service provider.

### KPI

For Key Performance Indicator, is an encrypted element that must be determined before launching an action, in order to assess its impact and determine the ROI (return on investment). The analysis takes into account several KPIs to estimate, for example, the number of clicks to calculate the open rate of an e-mailing in digital marketing or the rate of subscription to a product.

​

***

## The IAB lexicon

### Extra purpose

Through AppConsent, the customer can create customer-specific and non-IAB purposes to be included in the consent form or not, in the latter case they will be called floating purposes.

### Extra vendor

Through AppConsent, customers can add their non-IAB partners to the IAB by linking them with IAB or non-IAB purposes.

### GVL

For Global Vendors List, corresponds to the register of vendors participating in the framework of the TCF. All sellers, including sell-side platforms (SSP), demand-side platforms (DSP), ad servers, and data management platforms used on a publisher's site can apply to be part of the GVL.

### IAB

For Interactive Advertising Bureau is an international association created in 1998, bringing together Internet advertising players and whose mission is threefold: to structure the digital advertising market, promote its use and optimize its efficiency.

### Purpose

An IAB purpose is one of the 12 collection purposes defined by the IAB.

* Store and/or access information on a device
* Select basic ads
* Create a personalised ads profile
* Select personalised ads
* Create a personalised content profile
* Select personalised content
* Measure ad performance
* Measure content performance
* Apply market research to generate audience insight
* Develop and improve products
* Ensure security, prevent fraud, and debug
* Technically deliver ads or content

### Stack

A stack is a defined group of IAB purposes. In total, the IAB has defined 42 of them. This list is to be found on \[the IAB website]\([IAB Europe Transparency & Consent Framework Policies – IAB Europe](https://iabeurope.eu/iab-europe-transparency-consent-framework-policies/)).

### TCF

For Transparency & Consent Framework developed under the aegis of IAB Europe, proposes common rules to be adopted when processing personal data or accessing and / or storing information on a user's terminal , such as cookies, advertising identifiers, device identifiers and other tracking technologies. The aim is therefore to provide users with greater transparency on the use which is made of their personal data, as well as to collect their consent and transmit it to all the advertising actors identified in the GVL. In practice, the IAB Framework functions as a system for communicating the state of user consent between first parties (i.e. publishers), third parties (i.e. advertisers) and the consent management provider (i.e. CMP) used on the Part 1 website.

### Vendors

In the IAB Framework, these are the third party advertisers with whom the publisher has chosen to partner. Sellers post third party content on the publisher's website or application. They are the ones who place marketing cookies or trackers on the end user's browser or application, in order to display relevant advertisements to potential customers.

***

## Keywords related to our activities

### Blockchain

Developed in 2008, the blockchain is primarily a technology for storing and transmitting information. This technology offers high standards of transparency and security because it operates without a central control unit. More concretely, the blockchain enables its users - connected in a network - to share data without intermediaries.

### Environment Centric

Understanding of the environmental impact of products and technological infrastructures as soon as they are built.

### Privacy by default

The controller must ensure the highest level of protection for data subjects by default, which implies that security and protection measures must be taken systematically in the event of processing involving personal data.

### Privacy by design

A concept that requires companies to integrate the principles of the GDPR into the design of a project, service, or any other tool related to the handling of personal data. The idea is to impose that every new technology designed to handle personal data must be designed to provide a high level of data protection.

### Privacy by security

All data collected is anonymized, encrypted, and hashed, which ensures security in processing and data integrity.

### UX design

User Experience Design, is a set of methods whose objective is to place the human being at the heart of the design process by identifying his needs and constraints in a given context.

***

## Our features

### A/B tests

A/B testing is a process for testing the impact of a change in the version of a variable on the achievement of an objective (click, validation, etc.).

### Cohorts

This feature allows you to present a consent window to a specific group of people by knowing their identifiers.

### Consent Guard

This feature allows you to do a first level scan of the cookies deposited on a website.

### External ID’s

With this feature, the customer can choose the identifier associated with the user's consent.

### Floating purposes

This feature has the particularity of storing user consents in our blockchain, but the display belongs to the platforms of our customers. This feature can be useful to store the acceptance of the Terms of Use for example.

### Rollback

This feature allows customers to revert to earlier versions of their records with one click.


# FAQ

Questions about our market positioning / Questions about how AppConsent works

***

## Questions about our market positioning

* #### Are you a valid registered IAB CMP ?

YES. Our CMP\_ID is 2. You can consult this page to check our status : <https://iabeurope.eu/cmp-list/>

* #### Do you support only IAB Purposes ?

NO. Using the backoffice, you can add as many purposes you need. We also support higly sensitive purpose like Geolocation for advertising purpose. Check the iOS and Android section to learn more.

* #### Do you accept the scroll and click as valid event to get consent ?

From the 1st of april, as indicated by the CNIL when posting its new guidelines on the October 1st, scrolling on a website will no longer be considerated like a valid consent for using data.

Indeed, now the user must express his consent with a clear and precise act, like clicking on the button « Accept all ».

As a CMP, we never considered the scrolling as a valid act of consent, mais by using our callback, you could decice to still use this events to fetch consents from users.

From the 1st of april, it will no longer be possible.

* #### Banner, Notice, WebApp ? Well I'm a bit lost..

A CMP, for Consent Management Platform, is a technological platform specifically dedicated to the collection, recording and restitution of consents given by users in the field of personal data.

By using our CMP, you can control whether you want to use a banner to obtain consent in a first place. This banner is generally positioned at the bottom of the screen. What we call the notice, is the real visible part of the CMP, i.e. where the Internet user or mobile user will make his choices. Sometimes we call it WebApp to make a clear difference between the Web perimeter and in-App.

* #### Can you give me the magic recipe to get the highest possible consent rates?

YES. This is the recipe: Put a hard wall; meaning that the user has no choice to accept or configure. Blur the background. Don't hesitate to put a very large window. Change the configure button to a link. And put a lot of text before the button. Do not use a font size that is too large. Put a cross in the top right that is wired with an acceptance event. Do not forget to add an event wired to a 100px event and/or a click in the page. That's it, you just finished to cooking something that will give you a > 99% consent rate.

But in return, you will see your bounce rate increase a lot. By the way, it depends a lot on the shape and structure of your audience. Some websites are less impacted. But in the case of control by the regulator, the are a lot of chances that they don't see that as a valid way of getting consent. And the doctrine is simple: no consent, no right to use the data. And the vendors connected to the CMP may lose the right to process all the data already collected as they rely on consent.

At SFBX, we think that in order to cook something more clever, we all need to take into account: The user in the first place, because after all, we ask his/her data. But we also need to keep an eye on the reactions of the European data protection authorities (and especially the French CNIL) to build a balanced way of getting consents that :

* Respect the user
* Preserve as much as possible your turnover
* Respects the essence of the GDPR, and the comments of regulators.
* #### Do I need to present the CMP to the users who are not in the UE area?

GDPR aims to protect personal data for European users. You must display the CMP for all European country. For all other countries, there is no need to display the CMP, for the moment.

* #### How long are the consents kept?

The consents are kept for 12 months. After that, the user must give his consent again.

* #### It seems that you know a lot about users. How do you proceed?

As already mentioned above, we use proprietary UX approaches to understand user behavior when faced with CMP-typechoices. But also what they understand about the GDPR, what they expect. Focus groups, interviews, guerrilla tests. We do a lot of work on these topics. This work allows us to obtain consent rates that converge towards the maximum expected, including in frameworks that will be constrained by regulators. Example: Refuse/Accept button instead of Configure/Accept.

* #### In case of control from an ICO, do you help us?

YES. In each contract, you some days provisioned to assist you technically. We are not lawyers and we don't act as DPO. Never. But we offer workforce and tools (like APIs) in order to help you to prove that you have consents from your users.

Since the very first day, we use blockchain technology to prove consent. Beginning of 2020, we switched from Hyperledger to Chainsaw, our proper blockchain.

* #### What SLA do you offer ?

99.99%. We believe as we are directly responsible for your turnover and the comfort of your end-users, we have no alternative to offer this high standard of SLA.

We prefer to act as a member of the team rather than protect ourselves behind weaker SLAs. With this objective in mind, this has led us to put in place a whole battery of internal procedures, countermeasures in order to be able to fulfill such a promise.

* #### The 3rd Cookie dying, Safari ITP2, new Firefox privacy settings, upcoming Google Chrome behavior. How do you manage that?

SFBX was founded by two people acting in the data and ad tech companies for a wild. We see 3rd cookies efficacity and stability declining almost every day for a while. Thus, one of our first feature was to build a workaround to this. So we use LocalStorage in order to store consent status and cache information to speed up the CMP response time. Moreover, the entire platform is ID agnostic, meaning that we are ready for common login initiatives and using CRM IDs. We are even capable to do web2app consent Propagation.

* #### My best friend/DPO/CxO told me that the blockchain is not compatible with the GDPR so..how can you still use blockchain?

There is two main kinds of blockchain: Public and Private. It's true that Public blockchain raises many issues regarding GDPR/ePrivacy, especially when you need to honor the right to be forgotten. As blockchain is built to never forget something, it's a paradox.

But we use Private Blockchain. Meaning that Data Controller is not spread into the wild using nodes that we have not under control. All the nodes are running SFBX. We control the consensus, we control the participants. We control our ledger. And we never, ever store informations into our ledgers, we only store the proofs. And it makes a huge difference.

Moreover, this kind of architecture is preparing us for the future of Data. At SFBX, we are convinced that the data can't more circulate without either a consent or a legal base attached, tied to this Data. And in order to build the pipeline that will sustain this, private blockchain is very efficient.

* #### The same people told me that blockchains are too slow to operate in the media field yet.

We are working hard since two years to raise the bar of the scalability of our blockchain stack.

We process right now, in production, thousands of transactions per second. This means that when a end-user gives his/her consent, the proof of consent is available some ms after.

***

## Questions on the operation of AppConsent

* #### Can I customize everything in your CMP?

YES and NO. Using AppConsent, you can customize almost everything. Some things are not possible due to legal reasons but also to the way we understand the GDPR and the upcoming ePrivacy Regulation. As an example, it's not possible to force the status of our switch. This is something that will never pass a check from any European Regulator.

SFBX Official position on this: We believe that, as a market, we need to replace getting consents using scroll and click by other ways. It's not aligned with the core of the GDPR.

If you decide to carry on using scroll 'like events, we strongly advise you to test without scroll in order to do statistics on your consent rate.

{% hint style="danger" %}
**CAUTION**

The GDPR introduces the notion of accountabilty and co-responsabily. There is no doubt that in the very near future, the CMPs will no longer be able to act as sub-contractor. Thus, if we detect that something breaks the law, we'll invite you to contact us and to work on a more GDPR valid alternative.

Be aware that if consent is not valid, you're losing the right to process the underlying data. (If you are using the consent as your legal base)
{% endhint %}

* #### Does it take a long time to see the changes of a webApp in production?

**NO.** Just refresh your page in your browser and that's it. It's a matter of only hundreds of ms to populate a new build of our CMP WebApp.

* #### How are the translations managed?

AppConsent manages translations of 13 default languages but this number can be extended to all EU languages if needed.

* English (EN)
* French (FR)
* Spanish (SP)
* Italian (IT)
* Dutch (NL)
* Polish (PL)
* Portuguese (PT)
* German (DE)
* Bulgarian (BG)
* Czech (CS)
* Catalan (CA)
* Swedish (SV)
* Danish (DA)

Catalan, Swedish and Danish are currently only available for web notices.

As we can't know the user nationality, AppConsent CMP translation is based on the language used by the user on his browser or device:

**For the web desktop version**, the CMP is configured to be displayed in the language configured in the user's browser. For example, if the user is in the US but his browser is configured in French, the CMP will be displayed in French.

**For mobile and mobile web versions**, the CMP is configured to be displayed in the language configured in the device.

* #### Which KPIs do you offer in the dashboard?

You will get :

**Consent Rate :** the rate of Consent-In on \[consent in + consent out + consent mixed]

**Consent in:** number of positive consents on **all** the purposes of a notice

**Consent out:** number of negative consents (refusals) on **all** the purposes of a notice

**Consent mixed:** a mixed IN/OUT signal is raised when a user has a combination of switch that are both positives and negatives

**Bounce Rate:** It is the difference between new users and those who are not seen after their ID has been created. Even if it's not directly related to privacy-CMP KPIs, you should follow closely these KPIs

\*\*For Premium/Enterprise account only \*\* many others metrics are available on demand like unique users, average spent time before choice, granular deny for vendors, split by device, country..

***

## Have other questions?

If you have any further questions, send us an email to <support@sfbx.io> and we will be happy to answer you and certainly add your question to this page.


# Release Notes

***

{% content-ref url="/pages/8yAkLcxwr9rkqFkN8jvx" %}
[Web](/help/release-notes/web)
{% endcontent-ref %}

{% content-ref url="/pages/tvhEuAACp4kWTc0JcvBT" %}
[iOS](/help/release-notes/ios)
{% endcontent-ref %}

{% content-ref url="/pages/iMwFIPUYXO7kloMXbZri" %}
[Android](/help/release-notes/android)
{% endcontent-ref %}


# Web

### v33.1.0 - (August 12, 2026)

* A UTIQ integration has been added to the CMP. This feature can be configured from the configuration interface.
* Added an option to the `show` method to force the CMP to be displayed on excluded URLs
* In the mobile web view, the "Refine by partner" link was not centered correctly on Layer 2

### v33.0.0 - (July 22, 2026)

* To limit the amount of data downloaded, the CMP downloads a streamlined version of the notice configuration to the "Layer 1" display.
* The CMP can now inject scripts for the following events: display, init, and saveDone
* To optimize the display of text on "Layer 1," the CMP title has been moved by default next to the logo. A second configurable option in the configuration interface allows you to position the title and text to the right of the logo.
* A new method has been added to retrieve the current consent UUID. `__tcfapi('getUuid', 2, (uuid) => { console.log(uuid) })`
* From now on, you can configure the display of consent buttons in vertical orientation from the configuration interface.
* The CMP version now appears in the "My Privacy ID" menu on the "Settings" page.
* To better comply with Rule 7 of TCF compliance, we have modified how the "purposes" are displayed. From now on, this list will be displayed automatically without any action required from the user. Two display options are available: table mode and text mode. Both options can be configured via the configuration interface.
* A glitch appeared on the consent toggles on the "Settings" page when viewing the site on a mobile device
* On the consent buttons, the border color was not consistent with the button color when a user interaction occurred.

### v32.13.1 - (May 28, 2026)

* Bugfix : The CMP was no longer displayed in outdated / deprecated implementations.

### v32.13.0 - (May 27, 2026)

* Device Storage Disclosures updates under the Transparency and Consent Framework (TCF).

### v32.12.1 - (April 20, 2026)

* The first-party cookie feature could cause the CMP to crash if a cookie set by the host website used a non-compliant encoding.

### v32.12.0 - (April 2, 2026)

* A new feature `Excluded URLs from display` has been added to the configuration interface that allows you to specify URLs on which the CMP will not be displayed. This is useful for pages such as privacy pages.
* A new feature called `1st party user UUID` has been added to the configuration interface, which allows you to store the user UUID in a cookie and identify a user across subdomains
* You can now configure the color of the CMP overlay from the configuration interface
* Improved accessibility on Layer 1 of the CMP Clear
* Fixed: When the `cookie deletion on withdrawal of consent` feature was enabled, first-party cookies associated with a domain were not deleted properly.

### v32.11.0 - (January 22, 2026)

* TCF 2.3 amendments specifications have been added.
  * The new segment for disclosed vendors has been added.
* A new event named “new\_consent\_raised” has been added to the "dataLayer" variable to indicate whether a user has given or changed their consent.
* Bugfix: LiTE execution (script injection) was no longer performed after a second /hello

***

### v32.10.0 - (December 3, 2025)

* To comply with Transparency & Consent Framework policies, requiring that consent be changed to a single click, two buttons (“Refuse all and close” and “Accept all and close”) have been added to the settings page.
* On clear version, the illustration images can be customized from the configuration interface.
* The CMP can now display custom images (logo and illustrations) in webp format
* Fix: The Close button on the settings page did not reset the user's selection when the cmp was displayed for a consent change.
* Fix: Under certain conditions, the xchange script could not be injected into the website page.

***

### v32.9.1 - (August 4, 2025)

* Fix : the "language" property used to force the detection of the current language was no longer being applied.

***

### v32.9.0 - (July 3, 2025)

* A system for deleting 1st Party cookies ( exluding http only ) has been added when consent is withdrawn. This feature is deactivated by default, but can be activated on a source form in the configuration interface.
* A new "appconsent\_choice\_available" event is now injected in the dataLayer when a choice is available.
* The consent retention period label has been modified in layer 2 to make it easier for users to understand.
* fix bug: privacy widget could be displayed incorrectly with the Tailwind CSS library

***

### v32.8.1 - (May 26, 2025)

* Fix : The cmp was not displayed on sites with http\://, because the cmp used the subtle function of the crypto web api, which only works in a secure context (e.g. https).

***

### v32.8.0 - (May 12, 2025)[​](https://docs.sfbx.io/fr/help/release-notes#v3280---may-12-2025) <a href="#v3280---may-12-2025" id="v3280---may-12-2025"></a>

* Update iab framework to version 1.5.16 \[ Mandatory for all CMPs vendor]
* Cmp build size reduced to improve loading performance
* A search bar has been added to the list of vendors
* New customization properties have been added. It is now possible to customize :
  * hover color on consent buttons.
  * Layer 2 icon color (only available on CMP Clear).
* The error message indicating that Google Tag Manager (GTM) is initialized before Appconsent CMP is now transformed into a warning message.
* Fix: The size of the logo has been fixed to no longer impact performance when displaying the CMP.
* Fix: On the Classic version of CMP, the bottom banner mode was not displayed correctly. The consent buttons on Layer 1 no longer take up the full width of the screen.

***

### v32.7.1 - (February 27, 2025)[​](https://docs.sfbx.io/fr/help/release-notes#v3271---february-27-2025) <a href="#v3271---february-27-2025" id="v3271---february-27-2025"></a>

* Fix : Sometimes clicking on privacy widget failed to show the CMP on layer1
* Fix : Deployment of version 32.7.0 failed due to a CDN cache expiration issue ( GCP )

***

### v32.7.0 - (February 25, 2025) - Rollbacked[​](https://docs.sfbx.io/fr/help/release-notes#v3270---february-25-2025---rollbacked) <a href="#v3270---february-25-2025---rollbacked" id="v3270---february-25-2025---rollbacked"></a>

* 38 new languages have been added. These languages can be selected from the configuration interface

***

### v32.6.0 - (February 5, 2025)[​](https://docs.sfbx.io/fr/help/release-notes#v3260---february-5-2025) <a href="#v3260---february-5-2025" id="v3260---february-5-2025"></a>

* From now on, the fonts used in the CMP are hosted by SFBX.
* Apple Distraction Control detection is now performed on the same session.
* Error handling on addEventListener implementations has been improved
* Removal of the aria role attribute from the CMP iframe to improve accessibility score ( Core Web Vitals )
* The deprecated targetCountries property is now removed from CMP
* It is now possible to customize the color of links on the CMP from the configuration interface.
* Fix : A visual bug appears on the list of vendors with a low number of vendors.

***

### v32.5.2 - (December 18, 2024)[​](https://docs.sfbx.io/fr/help/release-notes#v3252---december-18-2024) <a href="#v3252---december-18-2024" id="v3252---december-18-2024"></a>

* TCF compliance: The text on the use of data storage has been improved on the vendor page.
* TCF compliance: An explanation of the presence of legitimate interests has been added to layer 1.

***

### v32.5.1 - (December 11, 2024)[​](https://docs.sfbx.io/fr/help/release-notes#v3251---december-11-2024) <a href="#v3251---december-11-2024" id="v3251---december-11-2024"></a>

* TCF compliance has been improved on the vendors page and the privacy widget.
* Fix : The scrollbar is now unlocked after the consent
* Fix : AppConsent CMP is once again compatible with older implementations

***

### v32.5.0 - (November 14, 2024) - Rollbacked[​](https://docs.sfbx.io/fr/help/release-notes#v3250---november-14-2024---rollbacked) <a href="#v3250---november-14-2024---rollbacked" id="v3250---november-14-2024---rollbacked"></a>

* Link colours can now be customised from the configuration interface
* CMP blocking with Apple Distraction Control is now detected. We have provided a new callback named adcDetected to allow you to react to a block.
* The targetCountries property has been removed from the configSFBXAppConsent configuration. From now on, to target one or more countries, please configure the ‘GDPR extra countries’ property available in the notice configuration form.

### v32.4.0 - (August 22, 2024)

* TCF 2.2 amendments specifications have been added \[TCF v2.2 Policies amendments: introduction of new Special Purpose 3 - "Save and communicate privacy choices" ]
  * The consent string retention period is now displayed in layer 2
  * The special purpose 3 has been added
  * Upgrade from TCF policies to version 5 (already implemented through gvl 2024-07-18 )
* Fix UI: A scroll bar appeared in the success screen on the Clear version

***

### v32.3.0 - (June 18, 2024)

* The forceGDPRApplies, urlRedirect, targetCountries and privacyWidget properties can be configured from the configuration interface.
* LiTE can now be configured and injected after the user's choice under consent, no-consent ( including datawall mode).
* Fix bug : On consent reacquisition, url redirection did not work

***

### v32.2.0 - (May 21, 2024)

* Google Basic Consent Mode has been added.
* It is now possible to activate TCF compatibility mode with Google Consent Mode (GCM)
* An error log is now displayed in the browser console when the CMP is initialised after a Google Tag Manager tag.

***

### v32.1.0 - (April 18, 2024)

* Adding Catalan ( CA ) , Suedish ( SA ) and Danish ( DA)
* ATPv2 : New version of additional consent released
* Update GCMv2 : Now full status is sent on update consent
* Fixed css class added to facilitate CMP customization
* Fix bug : The list of purposes in layer 1 did not take on the custom color in CMP classic.
* Fix bug : The title of the list of purposes was missing from the Portuguese translation.

***

### v32.0.1 - (March 01, 2024)

* Fix bug : In some cases, the AdSense flag (window\.adsbygoogle.pauseAdRequests) was not refreshed.

***

### v32.0.0 - (February 28, 2024)

* New implementation of web cmp has been added. Implementation with loader now deprecated, but still functional and maintened ( No breaking Change ).
* The enableGCM property can now be activated from the notice editing form in the configuration interface.
* A property has been added to the notice editing form to disable GCM in selected countries.
* An event named acextravendor\_denied\_ID is now added to GCM when an extra vendor is not granted.

***

### v31.1.3 - (February 05, 2024)

* New consent types for Google Consent Mode (GCM) v2 have been added :
  * ad\_user\_data
  * ad\_personalization

***

### v31.1.2 - (January 24, 2024)

* Improve performance : CMP makes fewer calls to the backend when GDPR is not applied
* Fix bug : Classic Template Only - Layer 1 hide the bottom buttons on small screens when « Display purposes on layer 1 » is activated.
* Fix bug : In some cases, the count of the number of vendors in layer 2, at the stack level, was not correct.
* Fix bug : Uncaught (in promise) error no longer appears in console in non-GPDR zone

***

### v31.1.1 - (January 08, 2024)

* Fix bug: Listener was undefined when gdprApplies was set to false
* Fix bug: window\.adsbygoogle.pauseAdRequests was not set correctly when gdprApplies was set to false

<details>

<summary>Old release-notes</summary>

### v31.1.0 - (December 21, 2023)

* The height of the CMP adjusts automatically based on the content.
* TCF2.2: UI & Texts Enhancements
* Now the gdprApplies is based on ip address of the user

***

### v31.0.1 - (December 14, 2023)

* New data format to improve notice configuration size

***

### v31.0.0 - (November 14, 2023)

* TCF 2.2 specifications have been added

***

### v30.4.1 - (October 30, 2023)

* Following the recent development regarding logo resizing and behavior, the UI of the CMP could sometimes be degraded.

***

### v30.4.0 - (October 19, 2023)

* The new Accept/Configure/Deny and Deny/Configure/Accept button configurations on the first display have been added.
* The switch icons on the settings page have been inverted to avoid misunderstandings.
* Logo display has been resized to improve image rendering
* GCM properties are no longer denied when the notice is displayed in a country outside the RGPD zone.
* Fix bug : Oppose legitimate interests button did not register correct consent status in consentstring
* Fix bug : A user's consent is not properly reflected in the interface between two websites with the same appkey.

***

### v30.3.1 - (April 27, 2023)

* In Bottom banner mode, a javascript crash appeared when the web CMP was used with the old implementation
* The command saveFloatingPurposes did not correctly save the values of the floating purposes in the localstorage

***

### v30.3.0 - (April 25, 2023)

* The IAB saveFloatingPurposes command is not persisted after a page refresh
* A vertical display mode for the buttons on the bottom banner has been added

***

### v30.2.0 - (March 29, 2023)

* Added horizontal banner display mode for the clear version
* The css class "button\_skip" was missing on the "Continue without accepting" button in clear version
* The action buttons in the "Classic" notice have been aligned to the right of the display to have the same display type as the Clear notice

***

### v30.1.0 - (February 14, 2023)

* Adding a preview mode which does not record the user's consent and which will be used for previews of notices on <https://app.appconsent.io/>.

***

### v30.0.0 - (November 24, 2022)

* The lazy option is activated by default
* GCM mode is now disabled by default
* Changing cmp cache duration
* Improving of compatibility with the old implementation
* Bug fixes for the new implementation

***

### v29.0.0 - (September 08, 2022)

* The implementation has been simplified.
  * Only the loader script and the new configuration variable of the cmp need to be implemented.
* Compatibility with the old implementation has been maintained.

***

### v28.11.1 - (July 06, 2022)

* Add an option to open Privacycenter from the text
* Add configurable URL redirection when clicking on buttons
* Multiple bugfixes
* Improve performance

***

### v28.10.4 - (June 13, 2022)

* Release clear template
* Update IAB TCF framework to v1.4.0
* Use day-accurate creation and update date for TCString
* Possibility to add regexp to hide CMP on matching URLs

***

### v28.7.16 - (May 30, 2022)

* No more usage of `eval` function in dependencies

***

### v28.7.15 - (February 11, 2022)

* Added a cache on backend call results to improve global usage performance

***

### v28.7.12 - (December 21, 2021)

* Add a method to manipulate consents without displaying the CMP
* Replace "continue without accepting" by a closing cross when CMP is displayed on a device using Italian language

***

### v28.6.0 - (November 17, 2021)

* Add Client-Origin header to all HTTP requests
* Reset store if consent is expired
* Different type for REFUSE\_ALL and CONTINUE\_WITHOUT\_ACCEPTING actions
* Calculate consent type on HELLO action

***

### v28.5.2 - (October 18, 2021)

* Display the link to the list of features

***

### v28.5.1 - (September 14, 2021)

* Set correct CMP id and version

***

### v28.5.0 - (August 18, 2021)

* New CMP methods are added: checkForUpdate, presentNotice, setExternalIds, saveExternalIds, getExternalIds,extraFloatingAllowed, isFloatingNeedUpdate, saveFloatingPurposes
* removeEventListener callback should be called with boolean instead of null
* ship2 request sends page url
* Static class name for modal banner

***

### v28.4.0 - (June 10, 2021)

* gdprApplies init param

***

### v28.3.0 - (May 27, 2021)

* Grant legitimate interest on fakedeny
* Language param
* Upgrade @iabtcf packages
* AMP banner/modal display bugfix
* Gray SFBX logo

***

### v28.2.0 - (May 04, 2021)

* List of bugs and features deployed :
* Created date and lastUpdated date in the consent string
* Custom CSS for AMP
* Modal mode for an AMP
* SFBX Copyright
* Colors & images customisation

***

### v28.1.0 - (April 21, 2021)

* Created date and lastUpdated date in the consent string
* New privacy widget logo
* Encode language in the consent string
* Remove circular dependencies
* Enable Legitimate Interest on REFUSE\_ALL / SKIP
* Skip link added for banner mode gdprApplies
* List of internal improvements :
* IN-90 New versionning system

***

### v28 - (March 23, 2021)

* Disable legitimate interest on DENY ALL

</details>


# iOS

### 6.0.2 ( June 04, 2026)

* fix:
  * Added transparency background property.

### 6.0.1 (March 10,2026)

* feat:
  * iOS: New hybrid architecture using our core web technology.&#x20;
  * new repo : <https://gitlab.datalf.chat/customers/appconsent-ios-unified>

***

### 4.9.12 tvOS  (July 31, 2026)

```
* feat:
    - tvOS:
        - Added the missing "see all partners and the scope of your consent" row at the bottom of layer 2, opening the full partners list. This aligns the tvOS layer 2 with the Android SDK.
* improvements:
    - tvOS:
        - The partners list now opens on the most populated tab (IAB or Other) instead of always defaulting to the first one.

* cmp version 132
```

###

### 4.9.11 (January 21, 2026)&#x20;

* feat:
  * tvOS:
  * iOS:
    * Added support for TCF 2.3
* cmp version 131
* Note: Last version with features. Only critical fixes will be integrated in v4.X.X branch. Please switch to new v6.0.0 version "UnifiedSDK"&#x20;

***

### 4.9.10 (November 14, 2025)

* improvements:
  * tvOS:
  * iOS:
    * Improve IDFA flow to prevent early update
* cmp version 130

***

### 4.9.9 (November 13, 2025)

* improvements:
  * tvOS:
    * Prevent back action when on layer1
    * Now checkForUpdate detect IDFA Reset
* fixes:
  * iOS:
    * Prevent overide of setExternalIDs when using it multiples times.
    * Now checkForUpdate detect IDFA Reset
    * Reduce network bandwith with some /hello calls
* cmp version 129

***

### 4.9.7 (October 31, 2025)

* improvements:
  * tvOS:
    * Focus engine improvements:
      * The appearance and disappearance of the initial button layout on certain views didn’t feel smooth because of how the focus engine works. This effect has now been minimized.
* cmp version 128

***

### 4.9.6 (October 29, 2025)

* fixes:
  * tvOS:
    * Fixed a bug where navigating back from certain views caused the previously presenting view to be dismissed along with the current one.
    * Resolved an issue where some text color configurations were not applied correctly.
* improvements:
  * tvOS:
    * UI enhancements to all vectorial assets for tvOS. Some assets appeared pixelated.
* cmp version 127

***

### 4.9.5 (October 13, 2025)

* fixes:
  * tvOS:
    * A bug occurred while rendering the partner detail page due to incorrect processing of the `remove_legintables` value.
* improvements:
  * tvOS:
    * UI enhancements to grid, buttons, and radio buttons to align with the look and feel of other supported platforms.
* cmp version 126

***

### 4.9.4 (August 13, 2025)

* fix: Fixed an issue where the `checkForUpdate` method incorrectly returned true even when no updates were available.
* cmp version 125

***

### 4.9.3 (June 05, 2025)

* feat: Added full support for tvOS in our SDK. Includes dedicated screen layouts and an optimized user experience tailored for the platform.
* cmp version 124

***

### 4.9.2 (March 25, 2025)[​](https://docs.sfbx.io/fr/help/release-notes#492-2025-03-25) <a href="#id-492-2025-03-25" id="id-492-2025-03-25"></a>

* fix: Prevented the intermediate state from being returned when a toggle change is triggered, as it was not yet saved. Changes can only be finalized and saved using the "Save" button.
* fix: Optimized network load for better performance.
* cmp version 123

***

### 4.9.1 (March 11, 2025)[​](https://docs.sfbx.io/fr/help/release-notes#491-2025-03-11) <a href="#id-491-2025-03-11" id="id-491-2025-03-11"></a>

* fix: The 'IDFA' (when available based on the ATT response) was incorrectly overriding the 'IDFV' value during the consent save operation.
* fix: The IDFV explanation text on the Profile View was overflowing the frame and not fully visible.

***

### 4.9.0 - (June 21, 2024)

* feat: added support for custom dedicated endpoint in ACNotice initializer
* cmp version 121

***

### 4.8.3 - (May 29, 2024)

* fix: fixed format of Privacy Manifest, added Privacy Accessed API Type for UserDefaults
* cmp version 120

***

### 4.8.2 - (May 27, 2024)

* fix: fixed format of Privacy Manifest
* cmp version 119

***

### 4.8.1 - (May 17, 2024)

* feat: added a Privacy Manifest to the xcFramework file
* cmp version 118

***

### 4.8.0 - (May 03, 2024)

* feat: title color on intro and success page follows bannerTitleColor options in admin console
* feat: background color on success page is same as intro page
* cmp version 117

***

### 4.7.0 - (April 25, 2024)

* feat: added allConsentablesDisallowed function to ACNotice
* cmp version 116

***

### 4.6.1 - (April 19, 2024)

* fix: changed GeoIP check routine as it was occasionaly falsely reporting non-GDPR sources as GDPR
* cmp version 115

***

### 4.6.0 - (April 17, 2024)

* feat: added option to display buttons vertically on introduction page
* feat: added support for new color configuration options on introduction page buttons
* feat: added option to display introduction page in fullscreen
* feat: added a GeoIP check at SDK startup, to determine whether your users are eligible or not
* feat: added list of purposes on introduction page
* cmp version 114

***

### 4.5.0 - (February 06, 2024)

* feat: improved translations of VoiceOver prompts on switches
* fix: added a link accessibility trait on underlined text links
* fix: added XXX partners subtitle to accessibility elements
* cmp version 113

***

### 4.4.8 - (January 26, 2024)

* fix: layout issue on success page when displaying large description texts on smaller screens
* fix: fixed an occasional crash that happened mainly during UI testing, though it is unlikely to have happened in a production scenario
* cmp version 112

<details>

<summary>Old release-notes</summary>

### 4.4.7

* fix: fixed a regression preventing consents reporting from stacksAllowed to be correct when upgrading from AppConsent 1.3.x
* cmp version 111

***

### 4.4.6

* fix: fixed a regression introduced in version 4.4.2 that caused switches to be neutralled on the settings page when a previous consent was present
* cmp version 110

***

### 4.4.5

* fix: fixed bug causing main application status bar to change color after AppConsent window closed
* cmp version 109

***

### 4.4.4

* fix: fixed bug preventing save button to be activated when returning to settings page after a change
* cmp version 108

***

### 4.4.3

* fix: prevent a crash occuring whent Montserrat fonts where already loaded in the Bundle by another library.
* cmp version 107

***

### 4.4.2

* fix: calls to backend now always rely on IDFV instead of IDFA, IDFA is saved as an external id 'idfa' when ATT popup is accepted.
* cmp version 105

***

### 4.4.1

* feat: removed error messages popup dialogs, now failing silently and logging an error message instead
* cmp version 104

***

### 4.4.0

* feat: improved layout around logo on intro page
* chore: dropped support for iOS 11, minimum version is now iOS 12
* fix: fixed a bug occuring sometimes when using the remove\_\_legintables option causing tha CMP to display a state.consentstring missing popop and failing to get consent
* cmp version 103

***

### 4.3.0

* feat: added support for TCF 2.2
* feat: adding background color change on all pages
* fix: fixed the bottom of scrolling text being hidden behind buttons on the introduction
* cmp version 102

***

### 4.2.3

* fix: calls to backend now always rely on IDFV instead of IDFA, IDFA is saved as an external id 'idfa' when ATT popup is accepted.
* cmp version 106

***

### 4.2.2

* feat: inverting switch on/off icons
* cmp version 101

***

### 4.2.1

* fix: IABTCF\_AddtlConsent didn't display the proper list of google providers, when some providers where denied.
* cmp version 12

***

### 4.2.0

* fix: AppConsentDelegate.appConsentDidFinish() didn't trigger when leaving settings screen without saving.
* fix: AppConsentDelegate.appConsentDidFail() didn't trigger on some errors.
* feat: added AppConsentDelegate.appConsentGeolocationDidFinish() to monitor Geolocation screen completion success.
* feat: added AppConsentDelegate.appConsentGeolocationDidFail() to monitor Geolocation screen completion failures.
* obsoleted AppConsentDelegate appConsentWillAppear(), appConsentDidAppear(), appConsentWillDisappear() and appConsentDidDisappear(), these functions only trigger on the intro screen, and they lack completeness.
* obsoleted AppConsentGeolocationConsentDelegate, it lacks completeness and is not reliable, replaced by AppConsentDelegate appConsentGeolocationDidFinish() and appConsentGeolocationDidFail()
* obsoleted consentGiven(success: (() -> Void)?, failure: ((Error) -> Void)?), prefer using AppConsentDelegate
* obsoleted geolocationConsentGiven(success: (() -> Void)?, failure: ((Error) -> Void)?), prefer using AppConsentDelegate
* cmp version 11

***

### 4.1.1

* fix: fixed a crash occuring on iPad when trying to copy the device ID from the profile view in settings
* feat: added a public SFBXCopyright string propertu on ACNotice
* cmp version 10

***

### 4.1.0

* obsoleted ACNotice.presentNotice(force: viewController:), replaced by presentNotice(viewController:) and presentSettings(viewController:)
* ACNotice.presentNotice() and ACNotice.presentSettings() now return a Bool, true if AppConsent was displayed, false otherwise
* AppConsentDelegate behavior change: after a call to presentNotice() or presentSettings(), appConsentDidFinish() is now always called, even if Notice wasn't displayed
* cmp version 9

***

### 4.0.3

* fix: made presentNotice(force: viewController) public again, will obsolete in a later release
* cmp version 8

***

### 4.0.2

* fix: on Mac Catalyst, keep profile view within window bounds.
* cmp version 7

***

### 4.0.1

* feat: AppConsent is now a single library instead of separated AppConsentKit and AppConsentUIKitV3
* feat: Added support for tvOS (minimal version is tvOS 14)
* feat: updated store.js to 1.0.3
* feat: added statistics collection
* cmp version 6

</details>


# Android

{% content-ref url="/pages/ujfOVyeo2tKLOWETJn6S" %}
[UnifiedSDK](/help/release-notes/android/unifiedsdk)
{% endcontent-ref %}

{% content-ref url="/pages/QEoQYBbQj7YbtpgX4tZ5" %}
[(Old SDK) Mobile / Tablet](/help/release-notes/android/old-sdk-mobile-tablet)
{% endcontent-ref %}

{% content-ref url="/pages/M2J24FTPuWNNda8If9Rz" %}
[(Old SDK) TV](/help/release-notes/android/old-sdk-tv)
{% endcontent-ref %}


# UnifiedSDK

## Release Notes

***

## Version 6.3.1 - July 20, 2026

### Highlights

**More reliable consent screen on activity recreation** Screen rotation (and other activity recreations) no longer drops consent callbacks or duplicates the consent screen — see Bug Fixes below. No integration change is required.

### Improvements

* **Faster preferences screen (layer 2).** Optimised display flow with finer handling of the current consent state — the preferences screen opens faster. No integration change is required.
* **Ongoing quality and reliability improvements.** Continuous internal hardening to keep the SDK stable across devices — fully transparent to your integration.

### Bug Fixes

* **Consent callbacks no longer lost on screen rotation.** `onCmpDisplayed` / `onCmpNotDisplayed` / `onCmpClosed` are now delivered reliably, exactly once, even if the device is rotated while the consent screen is open.
* **No more duplicate consent screen.** With integrations that re-trigger the notice on activity recreation (e.g. re-running `initialize()` + `presentNotice()` after a rotation), requesting it again while it is already open no longer stacks a second screen nor re-fires its callbacks.
* **Extra (non-IAB) vendor consent is now filtered correctly.** A fix keeps the IAB consent data in sync with the dedicated filter methods for the extra vendors configured on your source, so their allowed state reads back as expected. No user re-consent and no integration change is required.

### Upgrade Notes

No integration changes are required — update the `io.sfbx.appconsent:unifiedsdk` version to `6.3.1` and you are done. The public API is unchanged; the consent-callback reliability fix and the extra-vendor read fix are both transparent to your code.

***

## Version 6.3.0 - June 15, 2026

### Highlights

**Your app stays visible behind the consent screen** When the CMP is displayed, the area around the consent UI is now transparent, so your app remains visible underneath instead of being hidden behind an opaque background. This gives a smoother, more integrated transition when the notice appears and closes. The consent screen itself — its layout, content and behaviour — is unchanged, and no integration change is required. If you want to change the color of the overlay, configure it from the “notice” in your dashboard

**Improved IAB TCF v2 conformance** The SDK now surfaces `IABTCF_DisclosedVendors` to downstream ad providers — a standard IAB-spec preference key indicating, per session, which vendors were actually disclosed to the user in the CMP UI. Ad SDKs that read this key from `SharedPreferences` (as per the IAB CMP API v2 spec) will now find a populated value alongside the existing TCF keys.

In parallel, the SDK aligns with the latest IAB spec by no longer tracking `IABTCF_UseNonStandardStacks` as a first-class typed preference — it has been deprecated by the IAB in favour of `IABTCF_UseNonStandardTexts` (already supported by the SDK). If your CMP web configuration still emits the legacy key, it will be stored as a plain `String` in `SharedPreferences` instead of being silently dropped.

### Improvements

* **Cleaner logs.** Errors that the SDK catches and handles internally are no longer logged at error level on every layer they pass through. A given failure is now logged once, at the boundary where it is reported to your app (via `onCmpOnError`), instead of producing repeated error lines in Logcat. This removes the noisy "log-and-rethrow" behaviour some teams reported.
* **Lighter, leaner SDK.** Internal restructuring of the SDK's modules reduces packaging overhead and streamlines dependency resolution, with no change to the integration surface. As always, the SDK ships with no third-party runtime dependencies.

### Upgrade Notes

No integration changes are required — update the `io.sfbx.appconsent:unifiedsdk` version and you are done. The new consent-screen background behaviour, the IAB TCF improvements, the logging changes and the internal restructuring are all transparent to your code.

Ad SDKs that already read the standard IAB TCF SharedPreferences keys will automatically benefit from the new `IABTCF_DisclosedVendors` value at the next consent save.

***

## Version 6.2.0 — June 02, 2026

### Highlights

**Lighter classpath** The SDK no longer depends on Jetpack Compose to render its consent screens, so Compose is no longer pulled into your app's dependency graph as a transitive dependency of the SDK. Apps that experienced obscure Compose-version-misalignment crashes triggered by an outdated Compose pin shipped by the SDK should see that entire class of issues disappear.

**Crash protection for downstream ad SDKs reading IAB TCF metadata** The IAB-mandated integer TCF keys (`IABTCF_CmpSdkID`, `IABTCF_CmpSdkVersion`, `IABTCF_PolicyVersion`, `IABTCF_gdprApplies`, `IABTCF_PurposeOneTreatment`, `IABTCF_UseNonStandardTexts`) are now stored as `Int` in `SharedPreferences`, as required by the IAB CMP API v2 spec. A one-shot retroactive migration runs at the next SDK init and fixes the prefs for users who already gave consent under the old behaviour — **no user re-consent required, and the consent string itself (`IABTCF_TCString`) is untouched**.

### Improvements

* **Faster vendor modal after the first open.** The vendor-detail `WebView` inside the consent screen is now created lazily on the first vendor click and kept alive across subsequent open/close cycles for the duration of the consent activity. The \~200–400 ms WebView and renderer-process bootstrap is paid once per session instead of once per vendor click.

### Bug Fixes

* Fixed a re-entrant emission inside the vendor modal dismissal sequence — under certain timing conditions, closing the modal programmatically could trigger two close events back-to-back.
* Fixed layout overlap with system bars in landscape on devices where the 3-button navigation bar is mounted on the side (or where gesture indicators sit on the left/right). The consent screen and the vendor sheet now reserve space for **all** system bars instead of only the top/bottom ones — previously, vendor footers and the sheet's rounded background could bleed under the side nav bar.

### Upgrade Notes

No code changes required on the integrator side. If your app uses Jetpack Compose, the SDK no longer interferes with the Compose version your app resolves to — version alignment is now entirely under your control.

The IAB TCF metadata migration runs automatically at the next SDK init for affected users — no integrator action required.

***

## Version 6.1.0 — May 12, 2026

{% hint style="warning" %}
Although this release ships as a **minor version**, two integration points have changed and require small adjustments in your app code
{% endhint %}

### Breaking Changes

1. **`onCmpClosed` and `onCmpNotDisplayed` callbacks now expose an optional reason.** Both signatures gain a single `AppconsentExceptions?` parameter — `null` for a normal flow, non-null when the callback was triggered by an error condition (offline, host unreachable, etc.). Adjust your lambdas to accept the parameter.
2. **Five new `AppconsentExceptions` subclasses** are introduced (see *Richer error reporting* below). If your error handling matches `AppconsentExceptions` **exhaustively** in a `when`, add the new branches.

{% hint style="info" %}
*Apps that don't match `AppconsentExceptions` exhaustively, and that already ignore the (previously zero) arguments of `onCmpClosed` / `onCmpNotDisplayed`, need no code change at all.*
{% endhint %}

### Highlights

**Improved offline detection** Airplane mode and loss of internet connectivity are now detected proactively. The SDK responds to consent actions immediately when the device is offline — without waiting for long network timeouts. End users get a faster, clearer experience, and your app receives an explicit error it can react to (for instance, to display a retry action).

**Richer error reporting** Five new error types let your app distinguish between specific failure modes instead of guessing from a generic timeout:

* *no network at all*, *the consent server is unreachable*, and *an unexpected connectivity issue* — for the offline / network family;
* *the backend rejected the save call* — the server-side error payload is now propagated to your code instead of being swallowed by a timeout;
* *a CMP JS bridge callback failed* — surfaces the underlying reason from the webview, useful for diagnostics when the consent UI signals an issue.

The CMP close / not-displayed callbacks also carry an optional reason — so your app can pick the right next step (retry, fallback, ignore…) without polling the SDK.

**Smaller integration footprint** The internal HTTP layer has been reworked. The SDK no longer pulls an external HTTP client into your classpath as a transitive dependency, reducing method count and lowering the chance of version conflicts in your host app.

### Improvements

* **Main-thread callbacks guaranteed.** All callbacks returned by the SDK are now delivered on the main thread, so you can safely update your UI from inside them without additional dispatching.
* **Cleaner shutdown of consent dialogs.** When the consent dialog is closed, the SDK no longer performs any further work on the dismissed view, reducing the risk of rare crashes on exit.
* **More predictable concurrent execution.** Internal task management has been reworked for clearer lifecycles and tighter control over parallel operations — cancellation and error handling are now more consistent, and ongoing work stops promptly when no longer needed.
* **Multi-process clarity.** When the SDK is initialized from a non-default process, a clear warning now explains the action required (`WebView.setDataDirectorySuffix(...)` before `initialize`).
* **Faster startup.** Internal initialisation has been streamlined to avoid redundant work and unnecessary background activity, resulting in a lighter SDK bootstrap.

### Internal improvements

These changes are not visible through the public API, but they harden the internals you depend on and are worth knowing about when reading stack traces or comparing versions:

* **Idempotent module wiring.** The SDK's internal dependency-injection layer is now guarded so that loading the same module from multiple entry points no longer re-registers providers — avoids subtle duplicate-state issues when the SDK is wired in from more than one place in a process.
* **Explicit override semantics in the internal wiring.** Duplicate registrations used to be silently skipped; the SDK now uses an explicit replacement path internally, which made our test setups clearer and easier to debug.
* **Cross-cutting utilities regrouped.** Helpers for process info, OS version checks, and internet connectivity — previously scattered across modules — are now consolidated into a single internal utilities layer. Same behaviour, single home, easier to follow when reading a stack trace.
* **Leaner internal state access.** Hot SDK entry points now resolve their dependencies lazily instead of re-looking them up on every call, removing redundant work on frequently-hit paths.
* **Quieter production logs.** Verbose internal tracing in production paths has been replaced by a lighter coroutine helper — logs stay informative without the noise.

### Bug Fixes

* Fixed rare crashes that could occur when a callback was delivered on a background thread to app code expecting the main thread.
* Fixed an issue where the consent dialog could keep processing events after being dismissed.

### Documentation

* The `SFBX` entry-point API is now fully documented (lifecycle, threading, network and multi-process expectations).
* Integration stubs and sample applications have been refreshed to reflect the current public API.

### Upgrade Notes

1. Update your `onCmpClosed` and `onCmpNotDisplayed` callbacks to accept a single `AppconsentExceptions?` parameter — ignore it for a regular close, or react to it as you see fit.
2. If you have an exhaustive `when` over `AppconsentExceptions`, add the five new branches: three connectivity ones (`AppConsentNoConnectivityException`, `AppConsentConnectivityHostUnreachableException`, `AppConsentConnectivityException`), `AppConsentSaveFailedException`, and `AppConsentCallbackJSOnError`.
3. No further code change is required to benefit from the offline detection — the SDK starts using the new error callbacks automatically.
4. Optional: a new `SFBX.initialize(configuration, onCmpReady, onCmpOnError)` overload (no `Context`) is now the recommended entry point when you initialize from `Application`, an `Activity`, or any call site that runs after the SDK has bootstrapped. Keep using `SFBX.initialize(applicationContext, configuration, …)` if you initialize from your own `ContentProvider` that may run before the SDK's bundled one — there the `Context` is still required.

## Version 6.0.0 — April 8, 2026

This is a major release introducing significant stability improvements, better error handling, and critical bug fixes. **This version contains breaking changes** — please review the migration notes in the documentation before upgrading.

### ⚠️ Breaking Changes

* Error handling during SDK initialization has been updated. The `onCmpOnError` callback now receives a typed `AppconsentExceptions` object, giving you more granular control over initialization failures. Update your error handling accordingly.

### What's New

* The SDK now throws a clear, catchable error during initialization if the JavaScript engine fails to start, making issues easier to diagnose.
* The consent clearing method now supports `onSuccess` and `onError` callbacks, giving you better control over the flow.
* A secure loader is now displayed while the CMP is being loaded, improving the perceived experience for your users.

### Bug Fixes

* **Fixed** — The consent window was being hidden when switching between apps. Users can now return to a pending consent dialog without issue.
* **Fixed** — On older Android versions, the WebView was not rendering fully. This affected a significant portion of devices and has been resolved.
* **Fixed** — Tapping the "Close" button on vendor URLs in the bottom modal was incorrectly closing the entire CMP. This no longer occurs.
* **Fixed** — Navigation bar insets are now correctly handled, preventing UI overlap on devices with gesture navigation.
* **Fixed** — A race condition could cause the SDK to initialize twice when accessed from multiple threads simultaneously. This has been resolved.

### Improvements

* Internal scope management has been optimized, resulting in better memory usage and overall SDK performance.

***

## Version 0.3.0-beta01 — February 16, 2026

### ⚠️ Breaking Changes

* The SDK now targets API level 34, in line with Google Play's updated requirements. Please update your project's `compileSdk` and `targetSdk` accordingly.

### What's New

* Updated TCF compliance to version 2.3, managed in coordination with our backend infrastructure.
* The SDK now detects if no WebView component is available on the device and stops initialization gracefully, preventing unexpected crashes.
* A warning is now raised if the SDK is used in a multi-process environment, helping you identify potential WebView conflicts early.
* UI insets are now managed in relation to the illustrations present in your Notice, ensuring a consistent and polished display across devices.

### Bug Fixes

* **Fixed** — In some cases, the CMP screen would not dismiss after the user completed their consent choices.
* **Removed** — Toast notifications for internal CMP events have been removed, as they were occasionally visible to end users.

### Improvements

* Logging has been improved for greater accuracy during debugging.
* JavaScript ↔ Kotlin communication flows have been optimized, along with improved coroutine and flow management for better runtime performance.

***

## Version 0.2.0-beta01 — December 17, 2025

### Internal Changes

* Internal SDK module restructuring. No functional changes. If you reference internal module paths directly in your build configuration, you may need to update them — refer to the migration guide for details.

***

## Version 0.1.0-beta02 — December 9, 2025

### Bug Fixes

* **Fixed** — The second consent layer (Layer 2) was not displaying when the CMP was opened for the first time.
* **Fixed** — The consent clearing flow was not completing correctly.

### Improvements

* Internal logs have been updated to better reflect SDK lifecycle events, making debugging easier.

***

## Version 0.1.0-beta01 — December 5, 2025 — First Public Beta

We're excited to release the first public BETA of our new Consent Management Platform SDK. This is an early release intended for testing and integration feedback. **The API is not yet stable and may evolve — including breaking changes — in future beta updates.**

We warmly encourage you to share your feedback with us.

### What's Included in this Release

* **SDK Initialization** — The main entry point is now available to set up and start the SDK in your application.
* **Consent UI Display** — A method to display the consent interface to your users at the right moment in your app flow.
* **Consent Status Retrieval** — You can now query whether consent has been granted for a specific vendor by ID.
* **Real-time Consent Updates** — A listener is available to receive notifications when a user updates their consent choices.
* **TCF v2.2 Compliance** — The SDK is compliant with the IAB Transparency and Consent Framework v2.2.
* **Documentation** — Initial API documentation is published and available.


# (Old SDK) Mobile / Tablet

### 5.9.0 (January 14, 2026)

* Feat: Integration of the new ***TCF 2.3*** requirements from the IAB.
* Improve: Added several security controls, improving the SDK's environmental impact related to network calls.

### 5.8.0 (November 25, 2025)

* Fix: Prevents the system from creating a separate process when executing an SDK activity.
* Feat: New display method that takes into account current “activity,” allowing users to take full advantage of the context UI
* Improve: Improved display performance on certain screens requiring data updates

### 5.7.0 (October 10, 2025)

* Fix: Rare crash fixed due to certain versions of misaligned libraries
* Chore: Now requires a **minCompilSdk of 33** due to some **aar-metadata.properties** intos androidX libraries (*among other things*)

### 5.6.0 (July 31, 2025)

* Feat: Adds QR Code popup to replace internal hyperlink viewer. This feature is available from SDK initialization.
* Fix: Some "spacer" depends of screen size

### 5.5.6 (June 06, 2025)

* Fix: Truncated popup when displaying layer 1 and success screen on Android AUTOMOTIVE API34-ext9

<details>

<summary>Old release-notes</summary>

### 5.5.5 (May 23, 2025)

* Chore: Core module updated (3.5.3)
  * Fix: Add explicit TLSv1.3 instead of TLS generic AND force using best protocol depend on available protocols on running device
  * Improve: Log information when unable to get GAID

### 5.5.4 - (October 23, 2024)[​](https://docs.sfbx.io/help/release-notes/index.html#554---october-23-2024) <a href="#id-554---october-23-2024" id="id-554---october-23-2024"></a>

* Fix: Add edge-to-edge display functionality to make CMP display fully compatible
* Improve: Clean code
* Chore: Core module updated

### 5.5.3 - (August 28, 2024)

* Fix: Upgrade Core module version that fixes GCM minified troubles
* Fix: The number of vendors is not displayed for STACKS

### 5.5.0 - (July 03, 2024)

* Feat: Adds a 30 mn cache when using the `checkForUpdate` method

### 5.4.0 - (June 18, 2024)

* Feat: Adding options using `<meta-data />`.
* Feat: Add `isAllConsentablesDisallowed`, `isAllVendorsDisallowed`, `isAllStacksDisallowed` & `isUserDenyAll` method
* Feat: Add `isAllConsentablesAllowed`, `isAllVendorsAllowed`, `isAllStacksAllowed` & `isUserAcceptAll` method
* Fix: Prevents CMP from being displayed with default values in the case of an appkey not found
* Fix: Vendor list display was truncated in landscape mode (we couldn't access the privacy policy link via this screen for the last vendor)
* Refacto: Deprecated methods `allConsentablesAllowed`, `allVendorsAllowed`, `allStacksAllowed` & `userAcceptAll`
* Improve: Significant graphics enhancement
  * Decrease in margin size when displaying CMP in popup mode
  * Significant improvements on different screen sizes and on tablets
  * Text size taken into account for all screen sizes for the title on layer 1
  * Improved images displayed on the success screen when activated
* Improve: New deprecated methods into AppConsentTheme to be removed in later versions
  * `iconDrawable` is now deprecated
  * `onboardingImage` is now deprecated
  * `iconUrl` is now deprecated
* Improve: Deleting unused resources
* Improve: Clean code
* Improve: Speed of CMP display after first user consent

### 5.3.0 - (March 27, 2024)

* Feat: Add vertical button option from ACConfiguration, to enable layer 1 buttons (accept all, reject all, configure) to be displayed vertically in portrait mode only; by default, they are displayed horizontally.
* Fix: Added a drawable missing in low dpi mode, to avoid using an inappropriate one
* Improve: Reduced SDK size

### 5.2.0 - (March 21, 2024)

* Feat: Add WebProxy util to check if web view component is available, enable & implemented on user device
  * The CMP cannot be used if the component is not available on the user's device.
* Feat: Displays the list of stacks, purposes, special purpose, feature, special feature and extra purpose used in the notice configuration.
* Feat: Add new GAID mechanism to avoid bad UUID generation from providers
* Feat: Add a GeoIP check at SDK startup, to determine whether your users are eligible or not
* Improve: Data categories used in conjunction with the purposes
* Improve: New system to display vendors number
* Improve: Avoid line separator on vendor's description from GVL
* Fix: Legitimate interest urls fully mapped (some vendor's url like pdf and json didn't redirect)
* Fix: Motorola UUID generation problems with Native UUID (0000-0000) - <https://github.com/google/gson/issues/2103>
* Chore: Upgrade protobuf library from 3.23.0 to 3.23.2
* Chore: Upgrade iab store from 1.0.4 to 1.1.0

### 5.1.4 - (January 22, 2024)

* Fix: Crash caused by java.util.MissingResourceException: Couldn't find 3-letter country code for ... at java.util.Locale.getISO3Country (Pseudo language XA - XB)
* Fix: Fixes a metric problem when clicking on the continue button without accepting.
* Fix: Add support to min screen width 600 dp to allow multiple client icon size
* Chore: Update release notes - add more explicit information's about translations issues
* Refactor: Logger Module - Change some class name
* Refactor: Change layout res to dimens res constraint to avoid duplication layout

### 5.1.3 - (December 22, 2023)

* Chore: Update internal Logger module
* Fix: RuntimeException when several WebView instances in different processes are used. Added SDK startup check and full explanatory log in the event of a problem when integrating a third-party library that runs before the application and uses a WebView in a dedicated process.
* Fix: Display size problem on tablet in Dialog mode
* Fix: Significant reduction in SDK size

### 5.1.2 - (December 12, 2023)

* Fix: A display problem occurred when the "Enable only consent as the legal basis for processing" field was activated. Legitimate interests were still displayed under certain conditions
* Fix: In some cases, we had difficulty detecting the language set on the user's device and displayed the default selected language
* Improve: Addition of new controls at SDK startup to prevent the SDK from initializing by default
* Improve: logs

### 5.1.1 - (November 20, 2023)

* Chore: Updating dependencies to make transitive dependencies consistent.\
  These dependencies are aligned to be compatible with compileSdkVersion 30.\
  Fix ==> error: resource android:attr/lStar not found.\
  Fix ==> error: resource android:color/system\_neutralX\_XXX not found.
  * androidx.appcompat:appcompat to 1.4.0-alpha03
  * androidx.core:core-ktx to 1.7.0-alpha01
  * com.google.android.material:material to 1.5.0-alpha02

### 5.1.0 - (November 15, 2023)

* Fix: Incorrect values (0) displayed for partners
* Fix: Flying mode
* Fix: Add Connectivity check to avoid RequestTimeOut if no Internet / NoConnectivityException
* Fix: Vendors policy urls didn't open
* Fix: Avoid multiple call to same WS at the same time
* Fix: Default Google Advertising UUID deleted from generated response (00000000-0000-0000-0000-000000000000)
* Fix: Remove Uncaught Exception handler
* Fix: Add banner background color
* Fix: UI: force rotate depends app
* Fix: Avoid multiple same activities onRecreate (configurationChanged, rotate)
* Feat: Add Logger submodule / Remove Timber
* Feat: Optimize speed initialization
* Clean: code

### 5.0.0 - (September 20, 2023)

* Fix: Rollback to Gradle 7
* Fix: Rollback to JAVA 11
* Fix: Rollback compatibilities with 1.8

### 4.0.1 - (September 18, 2023)

* Fix: Some crash when migrating from TCF2.1 to TCF2.2 from a previous consent

### 4.0.0 - (September 13, 2023)

* Feat: TCF2.2
* Feat: Full screen mode
* Refactor: New way to initialize the SDK
* Refactor: New configuration for enhanced scalability
* Feat: New documentations
* Fix: Visual enhancement on small devices with large visual adaptation

### 2.1.1 - (June 19, 2023)

* Fix: \[INCIDENT] - Crash on networking request

### 2.1.0 - (June 08, 2023)

* Fix: Upgrade Core to 1.3.1
  * Fix: \[AMAZON] - Fix a bug on Amazon Fire TV
  * Fix: \[INCIDENT] - Fix a bug when user change Ads setting on device
* Feat: \[UI] Change the order of icons in switches on layers 2

### 2.0.16 - (April 18, 2023)

* Fix: Upgrade Core to 1.2.42
  * Feat: improve ssl verification hostname
  * Feat: add consumer proguard rules
  * Feat: add new entry to sample
  * Fix: remove keys from sharedpreferences not just updated + add 5 keys missed from clean

### 2.0.15 - (April 11, 2023)

* Fix: UI problem when the CMP is displayed, the user could click outside and close it without has been given his consent

### 2.0.14 - (March 21, 2023)

* Fix: Unity specific crashes
* Fix: Crash on XChange product

### 2.0.13 - (March 15, 2023)

* Fix: Improvement of the initialization process

### 2.0.12 - (February 23, 2023)

* Fix: Crashes for the XChange version

### 2.0.11 - (February 22, 2023)

* Fix: Flutter specific crashes when we try to use it before completion

### 2.0.5 - (January 31, 2022)

* Feat: Add illustrated mode new ui

### 2.0.4 - (January 10, 2022)

* Fix: Rollback api

### 2.0.3 - (January 03, 2022)

* Fix: Improves the user experience in case there is no internet

### 2.0.2 - (December 28, 2021)

* Fix: Added eco-friendly features to avoid multiple network calls

### 2.0.1 - (December 08, 2021)

* Feat: Update of the list of regions that must apply GDPR
* Feat: Update of the text
* Feat: Visual improvement

### 2.0.0 - (November 25, 2021)

* Feat: Displaying the notice as a modal
* Feat: Display geolocation as a modal
* Feat: Color harmonization
* Feat: New graphical component and new design
* Feat: Change the grouping of consents in layer 2

### 1.1.11 - (November 08, 2021)

* Fix: Miscellaneous non visual enhancement

### 1.1.10 - (October 26, 2021)

* Feat: Eco-responsible code processing
* Feat: Loading information from the Notice when the application is launched

### 1.1.9 - (September 27, 2021)

* Fix: Various improvements and fixes

### 1.1.8 - (September 02, 2021)

* Fix: Different methods to know which type of consent, vendors and stack have been granted

### 1.1.7 - (August 05, 2021)

* Feat: Addition of different methods to know which type of consent, vendors and stack have been granted
* Feat: Automatic start of smartTraffik for the XChange product
* Feat: Code improvement

### 1.1.6 - (July 22, 2021)

* Feat: Added Continue without accepting on layer 1

### 1.1.5 - (June 14, 2021)

* Feat: Added various features to allow distinguishing which types of consent the user has consented to, the list of consents and the use of the advertising UUID

### 1.1.3 - (February 09, 2021)

* Feat: Add vendor cookie expiration for TCF v2.1

### 1.1.2 - (February 03, 2021)

* Feat: XChange Product Enhancement

### 1.1.1 - (January 19, 2021)

* Feat: Code improvement

### 1.1.0 - (December 09, 2020)

* Feat: Improved design on the introduction screen
* Feat: Added a new feature dedicated to the acceptance or not of the geolocation authorization
* Feat: Added new features to the XChange product (Smart-Traffik & Pickwell)

</details>


# (Old SDK) TV

### 5.9.0 (January 14, 2026)

* Feat: Integration of the new ***TCF 2.3*** requirements from the IAB.
* Improve: Added several security controls, improving the SDK's environmental impact related to network calls.

### 5.8.0 (November 14, 2025)

* Feat: Adds QR Code popup to replace internal hyperlink viewer. This feature is available from SDK initialization.

### 5.7.0 (October 10, 2025)

* Chore: Now requires a **minCompilSdk of 33** due to some **aar-metadata.properties** intos androidX libraries (*among other things*)

### 5.5.5 (May 23, 2025)

* Chore: Core module updated (3.5.3)
  * Fix: Add explicit TLSv1.3 instead of TLS generic AND force using best protocol depend on available protocols on running device
  * Improve: Log information when unable to get GAID

### 5.5.4 - (October 23, 2024)[​](https://docs.sfbx.io/help/release-notes/index.html#554---october-23-2024) <a href="#id-554---october-23-2024" id="id-554---october-23-2024"></a>

* Chore: Core module updated

<details>

<summary>Old release-notes</summary>

### 5.5.3 - (August 28, 2024)

* Fix: Upgrade Core module version that fixes GCM minified troubles

***

### 5.5.0 - (July 03, 2024)

* Feat: Adds a 30 mn cache when using the `checkForUpdate` method

***

### 5.3.0 - (June 21, 2024)

* Feat: Adding options using `<meta-data />`.
* Feat: Add `isAllConsentablesDisallowed`, `isAllVendorsDisallowed`, `isAllStacksDisallowed` & `isUserDenyAll` method
* Feat: Add `isAllConsentablesAllowed`, `isAllVendorsAllowed`, `isAllStacksAllowed` & `isUserAcceptAll` method
* Feat: Added a method for specifying the onboarding title from SDK configuration
* Fix: Prevents CMP from being displayed with default values in the case of an appkey not found
* Improve: Clean code
* Improve: Speed of CMP display after first user consent
* Refacto: Deprecated methods `allConsentablesAllowed`, `allVendorsAllowed`, `allStacksAllowed` & `userAcceptAll`

***

### 5.2.0 - (March 21, 2024)

* Feat: Add WebProxy util to check if web view component is available, enable & implemented on user device
  * The CMP cannot be used if the component is not available on the user's device.
* Feat: Displays the list of stacks, purposes, special purpose, feature, special feature and extra purpose used in the notice configuration.
* Feat: Add new GAID mechanism to avoid bad UUID generation from providers
* Feat: Add a GeoIP check at SDK startup, to determine whether your users are eligible or not
* Improve: Data categories used in conjunction with the purposes
* Improve: New system to display vendors number
* Improve: Avoid line separator on vendor's description from GVL
* Fix: Legitimate interest urls fully mapped (some vendor's url like pdf and json didn't redirect)
* Chore: Upgrade protobuf library from 3.23.0 to 3.23.2
* Chore: Upgrade iab store from 1.0.4 to 1.1.0

### 5.1.4 - (January 22, 2024)

* Refactor: Logger Module - Change some class name

***

### 5.1.3 - (December 22, 2023)

* Chore: Update internal Logger module

***

### 5.1.2 - (December 12, 2023)

* Fix: A display problem occurred when the "Enable only consent as the legal basis for processing" field was activated. Legitimate interests were still displayed under certain conditions
* Fix: In some cases, we had difficulty detecting the language set on the user's device and displayed the default selected language
* Improve: Addition of new controls at SDK startup to prevent the SDK from initializing by default
* Improve: logs

***

### 5.1.1 - (November 20, 2023)

* Chore: Updating dependencies to make transitive dependencies consistent.\
  These dependencies are aligned to be compatible with compileSdkVersion 30.\
  Fix ==> error: resource android:attr/lStar not found.\
  Fix ==> error: resource android:color/system\_neutralX\_XXX not found.
  * androidx.appcompat:appcompat to 1.4.0-alpha03
  * androidx.core:core-ktx to 1.7.0-alpha01
  * com.google.android.material:material to 1.5.0-alpha02

***

### 5.1.0 - (November 15, 2023)

* Fix: Incorrect values (0) displayed for partners
* Fix: Flying mode
* Fix: Add Connectivity check to avoid RequestTimeOut if no Internet / NoConnectivityException
* Fix: Vendors policy urls didn't open
* Fix: Avoid multiple call to same WS at the same time
* Fix: Default Google Advertising UUID deleted from generated response (00000000-0000-0000-0000-000000000000)
* Fix: Remove Uncaught Exception handler
* Fix: Add banner background color
* Feat: Optimize speed initialization
* Feat: Add Logger submodule / Remove Timber
* Feat: Add inner WebView to view policies privacy and legitimate interest
* Clean: code

***

### 5.0.0 - (September 20, 2023)

* Fix: Rollback to Gradle 7
* Fix: Rollback to JAVA 11
* Fix: Rollback compatibilities with 1.8

***

### 4.0.1 - (September 18, 2023)

* Fix: Some crash when migrating from TCF2.1 to TCF2.2 from a previous consent

***

### 4.0.0 - (September 13, 2023)

* Feat: TCF2.2
* Refactor: New way to initialize the SDK
* Refactor: New configuration for enhanced scalability
* Feat: New documentations

***

### 1.1.48 - (June 19, 2023)

* Fix: \[INCIDENT] - Crash on networking request

***

### 1.1.47 - (June 08, 2023)

* Fix: Upgrade Core to 1.3.1
  * Fix: \[AMAZON] - Fix a bug on Amazon Fire TV
  * Fix: \[INCIDENT] - Fix a bug when user change Ads setting on device

***

### 1.1.46 - (April 18, 2023)

* Fix: Upgrade Core to 1.2.42
  * Feat: improve ssl verification hostname
  * Feat: add consumer proguard rules
  * Feat: add new entry to sample
  * Fix: remove keys from sharedpreferences not just updated + add 5 keys missed from clean

***

### 1.1.45 - (March 21, 2023)

* Improves the performance of the display

***

### 1.1.44 - (March 15, 2023)

* Improvement of the initialization process

***

### 1.1.43 - (February 23, 2023)

* Fix crashes for the XChange version

***

### 1.1.42 - (February 22, 2023)

* Fix Flutter specific crashes when we try to use it before completion

***

### v1.1.31 - (February 23, 2022)

* refactor logger , AppConsentCore injection instance
* add UncaughtExceptionHandler to reset the core instance
* remove exitProcesses on sdk tv

***

### v1.1.22 - (February 11, 2022)

* add flags for high heap work and enable logs

***

### v1.1.21 - (February 10, 2022)

* refactor injection at core and tv injector

***

### v1.1.20 - (February 03, 2022)

* send floating purpose at core init if previous call failed

***

### v1.1.17 - (January 19, 2022)

* fix focus issues around notice settings

***

### v1.1.16 - (January 10, 2022)

* rollback api from 1.1.15 regarding the dns exception handling
* remove leanback transition from the activity and fragment themes regarding warning logs

***

### v1.1.15 - (January 03, 2022)

* catch offline exception on api

***

### v1.1.14 - (December 28, 2021)

* update min sdk to 21 - 5.0)
* fix setupCrashlytics
* update gdpr countries list
* fix notice cache

***

### v1.1.13 - (November 08, 2021)

* put default header with Client-Origin
* setup consent-expiration regarding type of consent

***

### v1.1.12 - (October 26, 2021)

* add cache methode to get notice

***

### v1.1.13 - (November 08, 2021)

* put default header with Client-Origin
* setup consent-expiration regarding type of consent

***

### v1.1.12 - (October 26, 2021)

* add cache methode to get notice

***

### v1.1.11 - (September 27, 2021)

* setup crashlytics and timber logs

***

### v1.1.10 - (September 02, 2021)

* fix allConsentablesAllowed , allStacksAllowed and allVendorsAllowed

***

### v1.1.9 - (July 26, 2021)

* implement AppConsent interface
* add save floating purposes
* add consentable , stack , vendors all allowed methods

***

### v1.1.8 - (April 28, 2021)

* fix focus issue on NoticeTvFragment

***

### v1.1.7 - (April 20, 2021)

* fix focus issue on notice save click
* implement appconsent-core 1.2.9

***

### v1.1.6 - (April 08, 2021)

* fix focus issue on notice save click

***

### v1.1.5 - (March 05, 2021)

* fix focus on first mandatory consentable button

***

### v1.1.4 - (March 05, 2021)

* fix google\_atp\_id serial name for reducer
* core v1.2.6

***

### v1.1.3 - (March 04, 2021)

* fix consent string encode with restrictions
* core v1.2.5

***

### v1.1.2 - (March 02, 2021)

* core 1.2.4
* add google atp id feature

***

### v1.1.1 - (March 01, 2021)

* fix ui constraints for consent descriptions item

***

### v1.1.0 - (February 26, 2021)

* migrate kotlin-android-extensions to view binding
* add cookie expiration in vendor detail view for TCF 2.1
* delete description already displayed above from ConsentableDetail header
* add legal dialog in ConsentableDetail
* fix leg vendor radio display
* encode ConsentString with flexible purposes

***

### v1.1.0-beta03 - (December 22, 2020)

* fix bugs and improve SDK

***

### v1.1.0-beta01 - (December 11, 2020)

* add saveExternalIds()
* fix bugs and improve SDK

***

### v1.0.0-RC07 - (November 25, 2020)

* update modifier of classes to internal which are unused by user
* add setExtraConsentableConsents(), extraVendorAllowed() and extraConsentableAllowed()

***

### v1.0.0-RC06 - (November 20, 2020)

* add a new AppConsentLogListener to log details about client navigation
* do not display partner view if no internet

***

### v1.0.0-RC05 - (November 19, 2020)

* fix issue when downgrading GVL version to notice

***

### v1.0.0-RC04 - (November 17, 2020)

* IABTCF\_gdprApplies depends on forceApplyGDPR and phone's locale

***

### v1.0.0-RC03 - (November 13, 2020)

* fix focus on mandatory fragment

***

### v1.0.0-RC02 - (October 29, 2020)

* catch crash and if app restart kill process

***

### v1.0.0-RC - (October 02, 2020)

* update libraries
* fix save consent with empty cache

***

### v1.0.0-beta05 - (September 22, 2020)

* add back buttons on many views
* add intermediate view on back pressed on notice EDIT mode
* hide second view on notice if empty
* fix focus issue on accept all and refuse all
* fix logo display on vendor list
* improve tab layout navigation

***

### v1.0.0-beta04 - (September 07, 2020)

* transform radio button Accept/Refuse All to button
* fix bug on object action
* improve remote theme customization

***

### v1.0.0-beta03 - (August 01, 2020)

* fix radio focus bug with first element
* add background while radio is focus
* add local traductions for 8 languages

***

### v1.0.0-beta02 - (July 01, 2020)

* fix bug when implement SDK UI and TV in same project

***

### v1.0.0-beta01 - (July 01, 2020)

* first beta

</details>


# Flutter

{% content-ref url="/pages/C7nEeWLShorfTQK9o9SC" %}
[AppConsent Clear (SDK)](/help/release-notes/flutter/appconsent-clear-sdk)
{% endcontent-ref %}

{% content-ref url="/pages/prEvrOg4WkmAI0kGHMsI" %}
[AppConsent Classic (SDK)](/help/release-notes/flutter/appconsent-classic-sdk)
{% endcontent-ref %}

{% content-ref url="/pages/GYXtjbRPQYKWodxVyg8H" %}
[AppConsent TV (SDK)](/help/release-notes/flutter/appconsent-tv-sdk)
{% endcontent-ref %}


# AppConsent Clear (SDK)

### 2.5.0 - (February 05, 2026) <a href="#id-2.5.0-05-fevrier-2026" id="id-2.5.0-05-fevrier-2026"></a>

* Update Android dependency to 5.9.0, see Android release notes for details

***

### 2.4.3 - (May 27, 2025) <a href="#id-2.4.3-27-mai-2025" id="id-2.4.3-27-mai-2025"></a>

* Update Android dependency to 5.5.5, see Android release notes for details

***

### 2.4.2 - (October 23, 2024)[​](https://docs.sfbx.io/help/release-notes/index.html#242---october-23-2024) <a href="#id-242---october-23-2024" id="id-242---october-23-2024"></a>

* Update Android dependency to 5.5.4, see Android release notes for details

***

### 2.4.1 - (August 29, 2024)

* Update Android dependency to 5.5.3, see Android release notes for details

***

### 2.4.0 - (July 05, 2024)

* Update Android dependency to 5.5.0, see Android release notes for details

***

### 2.3.0 - (June 25, 2024)

* Updated iOS dependency to AppConsent 4.9.0
* Added support to setup dedicated endpoint during init
* Added support to display notice in fullscreen

***

### 2.2.3 - (June 21, 2024)

* Update Android dependency to 5.4.0, see Android release notes for details

<details>

<summary>Old release-notes</summary>

### 2.2.2 - (May 30, 2024)

* Updated iOS dependency to AppConsent 4.8.3, see iOS release notes for details

***

### 2.2.1 - (May 28, 2024)

* Updated iOS dependency to AppConsent 4.8.2, see iOS release notes for details

***

### 2.2.0 - (May 17, 2024)

* Updated iOS dependency to AppConsent 4.8.1, see iOS release notes for details

***

### 2.1.7 - (January 30, 2024)

* Updated iOS dependency to AppConsent 4.4.8, see iOS release notes for details

***

### 2.1.6 - (January 24, 2024)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.3 to 5.1.4 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.1.5 - (December 22, 2023)

* chore: update changelog formatting

***

### 2.1.4 - (December 22, 2023)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.2 to 5.1.3
* Chore: \[ANDROID] Update internal Logger module
* Fix: \[ANDROID] RuntimeException when several WebView instances in different processes are used. Added SDK startup check and full explanatory log in the event of a problem when integrating a third-party library that runs before the application and uses a WebView in a dedicated process.
* Fix: \[ANDROID] Display size problem on tablet in Dialog mode
* Fix: \[ANDROID] Significant reduction in SDK size

***

### 2.1.3 - (December 18, 2023)

* Updated iOS dependency to AppConsent 4.4.7

***

### 2.1.2 - (December 13, 2023)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.1 to 5.1.2
* Fix: \[ANDROID] A display problem occurred when the "Enable only consent as the legal basis for processing" field was activated. Legitimate interests were still displayed under certain conditions
* Fix: \[ANDROID] In some cases, we had difficulty detecting the language set on the user's device and displayed the default selected language
* Improve: \[ANDROID] Addition of new controls at SDK startup to prevent the SDK from initializing by default
* Improve: \[ANDROID] logs

***

### 2.1.1 - (November 21, 2023)

* \[Android] Update SDK dependency to 5.1.1 (optimize transitive dependencies)

***

### 2.1.0 - (November 15, 2023)

* \[Android] Update SDK dependency to 5.1.0 (fix bugs & optimize speed initialization)

***

### 2.0.0 - (September 21, 2023)

* \[Android] Add the new TCF2.2 feature

***

### 1.2.2 - (April 11, 2023)

* \[Android] Upgraded Android native module.
  * Fix users could click outside CMP and close it

***

### 1.2.1 - (March 23, 2023)

* Updated README instructions

***

### 1.2.0 - (March 23, 2023)

* \[iOS] Updated multiple iOS native module.

***

### 1.1.2 - (March 23, 2023)

* Update README instructions

***

### 1.1.1 - (March 23, 2023)

* \[Android] Upgraded Android native module.

***

### 1.1.0 - (March 17, 2023)

* \[Android] Upgraded Android native module.
* Feat: New features added

***

### 1.0.2 - (February 23, 2023)

* \[Android] Upgraded Android native module.

***

### 1.0.1 - (January 25, 2023)

* \[Android] Fix bug

***

### 1.0.0 - (September 15, 2022)

* \[iOS] Updated multiple iOS native module.
* Bumped version to stable release.

***

### 0.1.3 - (July 25, 2022)

* Fix: Some bug

***

### 0.1.2 - (June 23, 2022)

* \[iOS] Updated multiple iOS native module.

***

### 0.1.1 - (June 10, 2022)

* Fix: Some bug

***

### 0.1.0 - (May 24, 2022)

* \[iOS] Updated multiple iOS native module.
* Feat: New features added
* Code improvement

***

### 0.0.4 - (May 13, 2022)

* Updated API documentation.

***

### 0.0.3 - (May 10, 2022)

* \[iOS] Updated multiple iOS native module.

***

### 0.0.2 - (April 14, 2022)

* Code improvement

***

### 0.0.1 - (April 13, 2022)

* Initial release for AppConsent Flutter framework.

</details>


# AppConsent Classic (SDK)

{% hint style="danger" %}
**NO LONGER MAINTAINED!**

This SDK is no longer maintained as it is not compliant with TCF2.2.
{% endhint %}

### 2.0.0 - (September 21, 2023)

* \[Android] Upgraded Android native module.

***

### 1.0.5 - (March 23, 2023)

* Update README instructions

***

### 1.0.4 - (March 23, 2023)

* Update README instructions

***

### 1.0.3 - (March 23, 2023)

* \[Android] Upgraded Android native module.

***

### 1.0.2 - (February 23, 2023)

* \[Android] Upgraded Android native module.

<details>

<summary>Old release-notes</summary>

### 1.0.1 - (January 25, 2023)

* \[Android] Fixed a bug when we try to interact with AppconsentClassic before it is fully initialized.

***

### 1.0.0 - (September 15, 2022)

* Updated multiple iOS native module.
* Bumped version to stable release.

***

### 0.1.3 - (July 25, 2022)

* Fix: Some bug

***

### 0.1.2 - (June 23, 2022)

* Updated multiple iOS native module.

***

### 0.1.1 - (June 10, 2022)

* Fix: Some bug

***

### 0.1.0 - (May 24, 2022)

* Updated multiple iOS native module.
* Feat: New features added
* Code improvement

***

### 0.0.4 - (May 13, 2022)

* Updated API documentation.

***

### 0.0.3 - (May 10, 2022)

* Updated multiple iOS native module.

***

### 0.0.2 - (April 14, 2022)

* Code improvement

***

### 0.0.1 - (April 13, 2022)

* Initial release for AppConsent Flutter framework.

</details>


# AppConsent TV (SDK)

### 2.4.0 - (February 05, 2026)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.5.5 to 5.9.0 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.3.3 (May 27, 2025)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.5.4 to 5.5.5 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.3.2 - (October 23, 2024)[​](https://docs.sfbx.io/help/release-notes/index.html#232---october-23-2024) <a href="#id-232---october-23-2024" id="id-232---october-23-2024"></a>

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.5.3 to 5.5.4 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.3.1 - (August 29, 2024)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.5.0 to 5.5.3 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.3.0 - (July 03, 2024)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.3.0 to 5.5.0 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.2.0 - (June 21, 2024)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.1.4 to 5.3.0 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

***

### 2.1.4 - (January 24, 2024)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.1.3 to 5.1.4 [Official Release notes Android](https://docs.sfbx.io/help/release-notes#514-22012024)

<details>

<summary>Old release-notes</summary>

### 2.1.3 - (December 22, 2023)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-tv" from 5.1.2 to 5.1.3
  * Chore: \[ANDROID] Update internal Logger module

***

### 2.1.2 - (December 13, 2023)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.1 to 5.1.2
* Fix: \[ANDROID] A display problem occurred when the "Enable only consent as the legal basis for processing" field was activated. Legitimate interests were still displayed under certain conditions
* Fix: \[ANDROID] In some cases, we had difficulty detecting the language set on the user's device and displayed the default selected language
* Improve: \[ANDROID] Addition of new controls at SDK startup to prevent the SDK from initializing by default
* Improve: \[ANDROID] logs

***

### 2.1.1 - (November 21, 2023)

* \[Android] Update SDK dependency to 5.1.1 (optimize transitive dependencies)

***

### 2.1.0 - (November 15, 2023)

* \[Android] Update SDK dependency to 5.1.0 (fix bugs & optimize speed initialization)
  * Fix: \[Android] fix bugs
  * Feat: \[Android] optimize speed initialization

***

### 2.0.0 - (September 21, 2023)

* Feat: \[Android] New TCF2.2 feature

***

### 1.0.1 - (March 23, 2023)

* Chore: Update README instructions
* Clean: Decrease plugin size

***

### 1.0.0 - (March 23, 2023)

* Feat: New version of the native plugin.
* Fix: Improvement of the plugin initialization process

***

### 0.1.6 - (February 23, 2023)

* Feat: New version of the native plugin.

***

### 0.1.5 - (January 25, 2023)

* Feat: Patches related to wording

***

### 0.1.4 - (January 25, 2023)

* Fix: Bug when we try to interact with plugin before it is fully initialized.

***

### 0.1.3 - (July 25, 2022)

* Fix: Some bug

***

### 0.1.2 - (July 01, 2022)

* Feat: New version of the native plugin.

***

### 0.1.1 - (June 10, 2022)

* Feat: New version of the native plugin.

***

### 0.1.0 - (May 24, 2022)

* Feat: New features

***

### 0.0.3 - (May 17, 2022)

* Feat: New features
* Fix: some tests in example package

***

### 0.0.2 - (May 13, 2022)

* Feat: Improved intialization method
* Feat: Improved API docs.

***

### 0.0.1 - (April 14, 2022)

* Initial release for AppConsent Flutter framework.

</details>


# ReactNative

{% content-ref url="/pages/HwoWCSilcB7f65g0raj7" %}
[AppConsent Clear (SDK)](/help/release-notes/reactnative/appconsent-clear-sdk)
{% endcontent-ref %}

{% content-ref url="/pages/1Ow4wLG2BuddR6CR47Hl" %}
[AppConsent Classic (SDK)](/help/release-notes/reactnative/appconsent-classic-sdk)
{% endcontent-ref %}


# AppConsent Clear (SDK)

### 2.7.0 - (February 05, 2026)

* Chore: upgrade Android SDK natif from 5.5.5 to 5.9.0
* Chore: updated iOS AppConsent to 4.9.11, see iOS release notes for details

***

### 2.6.2 - (May 27, 2025)

* Chore: upgrade Android SDK natif from 5.5.4 to 5.5.5

***

### 2.6.1 - (October 23, 2024)[​](https://docs.sfbx.io/help/release-notes/index.html#261---october-23-2024) <a href="#id-261---october-23-2024" id="id-261---october-23-2024"></a>

* Chore: upgrade Android SDK natif from 5.5.3 to 5.5.4

***

### 2.6.0 - (August 29, 2024)

* Chore: upgrade Android SDK natif from 5.5.0 to 5.5.3

***

### 2.5.0 - (July 05, 2024)

* Chore: upgrade android SDK from 5.4.0 to 5.5.0

***

### 2.4.0 - (June 27, 2024)

* Chore: updated iOS AppConsent to 4.9.0, see iOS release notes for details

***

### 2.3.0 - (June 21, 2024)

* Chore: updated Android SDK to 5.4.0, see Android release notes for details

***

### 2.2.2 - (May 30, 2024)

* Chore: updated iOS AppConsent to 4.8.3, see iOS release notes for details

***

### 2.2.1 - (May 29, 2024)

* Chore: updated iOS AppConsent to 4.8.2, see iOS release notes for details

***

### 2.2.0 - (May 17, 2024)

* Chore: updated iOS AppConsent to 4.8.1, see iOS release notes for details

***

### 2.1.9 - (January 30, 2024)

* Chore: updated iOS AppConsent to 4.4.8, see iOS release notes for details

***

### 2.1.8 - (January 26, 2024)

* fix: marked consentGiven as obsolete, on iOS, implementation changed to match consentAlreadyGiven

***

### 2.1.7 - (January 24, 2024)

* feat: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.3 to 5.1.4

<details>

<summary>Old release-notes</summary>

### 2.1.6 - (December 22, 2023)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.2 to 5.1.3
* Chore: \[ANDROID] Update internal Logger module
* Fix: \[ANDROID] RuntimeException when several WebView instances in different processes are used. Added SDK startup check and full explanatory log in the event of a problem when integrating a third-party library that runs before the application and uses a WebView in a dedicated process.
* Fix: \[ANDROID] Display size problem on tablet in Dialog mode
* Fix: \[ANDROID] Significant reduction in SDK size

***

### 2.1.5 - (December 18, 2023)

* feat: updated iOS native dependency to 4.4.7

***

### 2.1.4 - (December 13, 2023)

* Chore: \[ANDROID] Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.1 to 5.1.2
* Fix: \[ANDROID] A display problem occurred when the "Enable only consent as the legal basis for processing" field was activated. Legitimate interests were still displayed under certain conditions
* Fix: \[ANDROID] Translation issues
* Improve: \[ANDROID] Addition of new controls at SDK startup to prevent the SDK from initializing by default
* Improve: \[ANDROID] logs

***

### 2.1.3 - (November 23, 2023)

* Chore: Updated README to add examples and more detailed information
* Chore: Updated example

***

### 2.1.2 - (November 20, 2023)

* fix: \[ANDROID] Update some dependencies to avoid problems when transitive dependencies are downloaded.
* Chore: Update "com.sfbx.appconsent:appconsent-ui-v3" from 5.1.0 to 5.1.1

***

### 2.1.1 - (November 16, 2023)

* fix: \[ANDROID] rollback of AndroidManifest's packagename attribute, as it causes an undefined name when we try to use the

***

### 2.1.0 - (November 15, 2023)

* feat: \[ANDROID] added getExternalId method
* feat: \[ANDROID] added saveExternalId method
* feat: \[ANDROID] added setExternalId method
* feat: \[ANDROID] updated native SDK dependency to 5.1.0

***

### 2.0.2 - (October 30, 2023)

* feat: added getExternalId

***

### 2.0.1 - (October 20, 2023)

* feat: updated iOS native dependency to 4.4.2
* feat: added saveExternalId and setExternalId

***

### 2.0.0 - (September 21, 2023)

* feat: Add Android TCF2.2

***

### 1.2.4 - (October 20, 2023)

* feat: updated iOS native dependency to 4.2.3

***

### 1.2.3 - (April 19, 2023)

* fix: Updated Android dependency to 2.0.16
* fix: resolution of the obfuscation carried by the android library
* fix: Improvement of network exchanges

***

### 1.2.2 - (April 11, 2023)

* fix: Updated Android dependency to 2.0.15
* fix: UI prevent user click outside CMP
* feat: updated iOS native dependency to 4.2.0

***

### 1.2.1 - (March 24, 2023)

* chore: update readme instructions

***

### 1.2.0 - (March 23, 2023)

* feat: updated iOS native dependency to 4.1.0

***

### 1.1.7 - (March 22, 2023)

* fix: removed dependency on expo
* fix: replaced a fatal error throw by an error log to prevent a crash when CMP can't attach to viewController
* fix: dependency on Android 2.0.12 instead of 2.0.11

***

### 1.1.6 - (February 23, 2023)

* Updated Android dependency to 2.0.12.

***

### 1.1.5 - (February 22, 2023)

* Updated Android dependency to 2.0.11.
* this one corrects a crash at the launching of the application after the validation of the consents

***

### 1.1.4 - (September 26, 2022)

* Updated iOS dependencies to AppConsentKit 1.4.2.
* Updated iOS dependencies to AppConsentUIKitV3 2.2.2.

***

### 1.1.3 - (August 25, 2022)

* Added TypeScript declarations to function headers.
* Added TSDoc comments to all API functions.

***

### 1.1.2 - (August 18, 2022)

* Updated Android dependencies to 2.0.10.

***

### 1.1.1 - (June 29, 2022)

* Added side-effects declaration to package.

***

### 1.1.0 - (June 28, 2022)

* Upgraded to react 18.0.0 and react-native 0.68.0.

***

### 1.0.2 - (June 27, 2022)

* Configured linter and linted code.

***

### 1.0.1 - (June 24, 2022)

* Excluded some unwanted files from package.
* Added changelog.

***

### 1.0.0 - (June 24, 2022)

* Updated iOS dependencies to AppConsentKit 1.4.1.
* Updated iOS dependencies to AppConsentUIKitV3 2.2.1.

***

### O.1.6 - (May 25, 2022)

* Updated iOS dependencies to AppConsentKit 1.4.0.
* Updated iOS dependencies to AppConsentUIKitV3 2.2.0.
* fix consentableAllowedByIABId returned the wrong consentableType on Android.

***

### 0.1.5 - (May 10, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.11.
* Updated iOS dependencies to AppConsentUIKitV3 2.1.7.

***

### 0.1.4 - (March 29, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.3.

***

### 0.1.3 - (March 29, 2022)

* Updated Android dependencies to 2.0.9-react.

***

### 0.1.2 - (March 29, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.2.
* Updated iOS dependencies to AppConsentUIKitV3 2.1.4.

***

### 0.1.1 - (March 22, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.1.
* Support Podfile without use\_frameworks!.

***

### 0.1.0 - (March 03, 2022)

* first release.

</details>


# AppConsent Classic (SDK)

{% hint style="danger" %}
**NO LONGER MAINTAINED!**

This SDK is no longer maintained as it is not compliant with TCF2.2.
{% endhint %}

### 1.0.1 - (September 16, 2022)

* Updated iOS dependencies to AppConsentKit 1.4.2.
* Updated iOS dependencies to AppConsentUIKit 1.4.2.

***

### 1.0.0 - (June 29, 2022)

* Upgraded to react 18.0.0 and react-native 0.68.0
* Added typescripts declaration to package

***

### 0.9.17 - (June 24, 2022)

* Added changelog.

***

### 0.9.16 - (June 24, 2022)

* Updated iOS dependencies to AppConsentKit 1.4.1.
* Updated iOS dependencies to AppConsentUIKit 1.4.1.

***

### 0.9.15 - (May 25, 2022)

* Updated iOS dependencies to AppConsentKit 1.4.0.
* Updated iOS dependencies to AppConsentUIKit 1.4.0.

<details>

<summary>Old release-notes</summary>

### 0.9.14 - (May 10, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.11.
* Updated iOS dependencies to AppConsentUIKit 1.3.7.

***

### 0.9.13 - (March 29, 2023)

* Updated iOS dependencies to AppConsentKit 1.3.3.

***

### 0.9.12 - (March 29, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.2.
* Updated iOS dependencies to AppConsentUIKit 1.3.4.

***

### 0.9.11 - (March 24, 2022)

* Updated Android dependencies to AppConsent 1.1.22-react.
* Updated various dependencies.

***

### 0.9.10 - (March 22, 2022)

* Updated iOS dependencies to AppConsentKit 1.3.1.
* Support for iOS Podfiles withous use\_frameworks!.

</details>


# Unity

### 4.4.0 - (February 04, 2026)

* feat: iOS & Android: TCF2.3 compliance :rocket:
  * \[iOS]: migrate to the latest 4.9.11, see iOS release notes for details
  * \[ANDROID]: migrate to the latest 5.9.0, see Android release notes for details

***

### 4.3.1 - (May 26, 2025)

* Chore: \[ANDROID] Upgrade Android SDK from 5.5.4 to 5.5.5

***

### 4.3.0 - (October 24, 2024)[​](https://docs.sfbx.io/help/release-notes/index.html#430---october-24-2024) <a href="#id-430---october-24-2024" id="id-430---october-24-2024"></a>

* Chore: \[ANDROID] Upgrade Android SDK from 5.5.3 to 5.5.4
* Feat: \[ANDROID] Add FullScreen Mode to android (layer1)

***

### 4.2.2 - (August 29, 2024)

* Chore: \[ANDROID] Upgrade Android SDK from 5.5.0 to 5.5.3

***

### 4.2.1 - (August 02, 2024)

* fix: change default `forceApplyGDPR` from `true` to `false` to avoid displaying CMP in non-GDR regions

***

### 4.2.0 - (July 05, 2024)

* feat: Upgrade Android SDK from 5.4.0 to 5.5.0

***

### 4.1.0 - (June 25, 2024)

* feat: added support for dedicated endpoint on iOS
* feat: added support for fullscreen on iOS
* feat: updated iOS Native dependency to 4.9.0, see iOS release notes for details

<details>

<summary>Old release-notes</summary>

### 4.0.3 - (June 19, 2024)

* Chore: \[ANDROID] Upgrade Android SDK from 5.1.4 to 5.4.0

***

### 4.0.2 - (June 04, 2024)

* fix: updated iOS Native dependency to 4.8.3, see iOS release notes for details

***

### 4.0.1 - (March 28, 2024)

* Fix: The type or namespace name 'DllImportAttribute' could not be found
* Fix: The type or namespace name 'DllImport' could not be found

***

### 4.0.0 - (January 23, 2024)

* BREAKING\_CHANGE: Reformat methods to convention naming C#
* \[ANDROID] Upgrade to Android 5.1.4
* \[ANDROID] New feature to propose a callback to know when the user has completed the consent process.
* Add new method InitACNotice to separate Instanciation and Init (on Android)

***

### 3.1.3 - (December 22, 2023)

* \[ANDROID] Upgrade to Android 5.1.3
* Chore: \[ANDROID] Update internal Logger module
* Fix: \[ANDROID] RuntimeException when several WebView instances in different processes are used. Added SDK startup check and full explanatory log in the event of a problem when integrating a third-party library that runs before the application and uses a WebView in a dedicated process.
* Fix: \[ANDROID] Display size problem on tablet in Dialog mode
* Fix: \[ANDROID] Significant reduction in SDK size
* Fix: \[ANDROID] In some cases, we had difficulty detecting the language set on the user's device and displayed the default selected language

***

### 3.1.2 - (December 13, 2023)

* \[ANDROID] Upgrade to Android 5.1.2

***

### 3.1.1 - (November 21, 2023)

* \[ANDROID] Upgrade to Android 5.1.1
* \[ANDROID] Change module name from library imported : Unity -> appconsent-unity

***

### 3.1.0 - (November 20, 2023)

* \[ANDROID] Upgrade to Android 5.1.0

***

### 3.0.0 - (September 20, 2023)

* \[ANDROID] Upgrade to Android 5.0.0

***

### 2.0.1 - (September 20, 2023)

* \[ANDROID] Remove unused dependences

***

### 2.0.0 - (September 19, 2023)

* \[ANDROID] Upgrade sdk version to 4.0.1 (include TCF2.2)

***

### 1.0.16 - (September 15, 2023)

* \[iOS] Upgrade appconsent-unity-ios-bridge to version 1.0.2

***

### 1.0.15 - (April 11, 2023)

* \[ANDROID] Upgrade sdk version to 2.0.15
* \[ANDROID] New library version Unity-1.0.5.aar

</details>


