Consent management
Prerequisite
Consent Management is only available on JS ≥ 6.8.0, Android ≥ 3.4.0 and iOS ≥ 3.1.0.
If you use PA JS < 6.8.0 / Android < 3.4.0 / iOS < 3.1.0, please use legacy Privacy management methods.
If you want to use the ePrivacy exemption, please read this documentation first. Contact our support centre to request an activation process for the ePrivacy exemption.
What is "Consent Management"
"Consent Management" is the first building block of unified tagging methods across all Piano products.
You will be able to control the data collected, according to the different regulations you refer to. All in one place for all your Piano products.
In other words, it allows you to customize your tagging thanks to a precise management of the different measurement parameters (events and properties sent, trackers used, ...), in compliance with the regulations in force.
All the logic is explained in our Privacy center.
As a unified way to manage Privacy, “Consent Management” can be triggered from several Piano SDK.
You have two options:
-
You use several Piano Products
- You can manage consent for all products using the Piano Analytics SDK and this documentation.
- Or you can use the Composer SDK with this configuration.
Make sure that only one SDK triggers Privacy instructions.
-
You only use Piano Analytics
- Use this page to setup you Privacy instructions.
Key notion
A "Consent Mode" is a state in which you can have control over various items:
- The events / properties you want to send
- The value your
visitor_privacy_mode
andvisitor_privacy_consent
properties will take
Basically, we can say that a Consent Mode refers to a specific purpose (under GDPR).
Thanks to the visitor_privacy_mode
and visitor_privacy_consent
, you will be able to respect a perfect data governance by applying dedicated treatments depending on the mode the data has been collected.
Consent Modes
- Javascript
- Android (3.4.0+)
Consent Mode | setMode value | "_pcid" cookie | Visitor ID | visitor_privacy_mode | visitor_privacy_consent | Properties / events | Exclusion / Anonymization | Comment |
---|---|---|---|---|---|---|---|---|
Opt-in | opt-in | ✅ | ✅ | optin | true | all | ❌ | |
Exempt | essential | ✅ | ✅ | exempt | false | some | ❌ | Requires a specific configuration as explained in our Privacy center |
Opt-out | opt-out | ❌ | ❌ | optout | false | some | ✅ | All events will be excluded and anonymized. More information in our Privacy Center |
Extended Opt-out ≥ 6.13.0 | opt-out | ❌ | ❌ | extended-optout | false | all | ✅ | enableExtendedOptout SDK configuration needed. All events will be anonymized thanks to the "Contains personal data" flags. More information in our Privacy Center |
Custom | custom | you decide | you decide | you decide | you decide | you decide (same base as essential mode) | ❌ | Some cookies, properties and events are included by default to a custom mode |
Consent Mode | pianoConsents.set() value | Visitor ID | visitor_privacy_mode | visitor_privacy_consent | Properties / events | Exclusion / Anonymization | Comment |
---|---|---|---|---|---|---|---|
Opt-in | ConsentMode.OPT_IN | ✅ | optin | true | all | ❌ | |
Exempt | ConsentMode.ESSENTIAL | ✅ | exempt | false | some | ❌ | Requires a specific configuration as explained in our Privacy center |
Opt-out | ConsentMode.OPT_OUT | ❌ | optout | false | some | ✅ | All events will be excluded and anonymized. More information in our Privacy Center |
Custom | ConsentMode.CUSTOM | you decide | custom | you decide | you decide | ❌ | Some cookies, properties and events are included by default to a custom mode |
Each mode has by default its own rules.
For JS SDK, you can check which storage keys (cookies), properties and events are included by default in the dedicated article.
For Android SDK, the default configuration is the same as the Privacy modes. Please note that only Optin, Exempt and Optout are available for Consent feature.
You can also find the list of our cookies and storage keys in the Cookies & storage article.
Extended opt-out
Since PA JS 6.13.0, you can turn on "Extended opt-out". This feature anonymously collect exhaustive data for opted-out visitors. See how it works in our Privacy center.
To enable it, you should set the SDK configuration enableExtendedOptout
to true
.
This will change the behavior of opted-out visitors, who will now send every events and properties, with an anonymized visitor ID (idclient
).
Pre-requisite
- Javascript
- Android (3.4.0+)
- Apple
- Flutter
The configuration of the Consents feature uses the global object window.pdl
, that must be defined and populated before the loading of the SDK.
<script>
window.pdl = window.pdl || {};
...
</script>
...
<script src="piano-analytics.js"></script>
The configuration of the Consents feature requires the dependency available here.
The Piano Consents SDK is available as an AAR via MavenCentral. To add dependencies, open your project's build.gradle
/build.gradle.kts
and update dependencies
block:
dependencies {
...
implementation("io.piano.android:consents:VERSION")
}
The configuration of the Consents feature requires the dependency available here.
Add the piano_consents
package to your pubspec.yaml
file:
dependencies:
piano_consents: ^1.0.0
The configuration of the Consents feature requires the dependency available here.
CocoaPods
Add the following lines to your Podfile.
use_frameworks!
pod 'PianoConsents', '~>1.0.6'
Swift Package Manager
Add the component PianoConsents from the repository:
URL: https://gitlab.com/piano-public/sdk/ios/packages/consents
Version: 1.0.6
You can then import the dependency:
import PianoConsents
Activating the Consents feature
- Javascript
- Android (3.4.0+)
- Apple
- Flutter
In order to activate the Consents feature, you need to set the requireConsent
variable to 'v2'
:
window.pdl = window.pdl || {};
window.pdl.requireConsent = "v2";
window.pdl.consent = {};
// If you use Analytics only
window.pdl.consent.products = ["PA"];
In order to activate the Consents feature, you need to give the PianoConsents
instance to your PianoAnalytics
instance:
val pianoConsents = PianoConsents.init(
context,
ConsentConfiguration(
requireConsent = true // `false` means "disable the feature"
)
)
PianoAnalytics.init(applicationContext, configuration, pianoConsents)
In order to activate the Consents feature, you need to PianoConsents
instance before PianoAnalytics
instance:
import 'package:piano_consents/piano_consents.dart';
...
class _MyAppState extends State<MyApp> {
final _pianoConsents = PianoConsents(
requireConsents: true,
defaultPurposes: {
PianoConsentProduct.pa: PianoConsentPurpose.audienceMeasurement
}
);
final _pianoAnalytics = PianoAnalytics(...);
void initState() {
super.initState();
initPlugins();
}
Future<void> initPlugins() async {
...
await _pianoConsents.init();
await _pianoAnalytics.init();
...
}
...
}
In order to activate the Consents feature, you need to init PianoConsents
instance:
let configuration = PianoConsentsConfiguration(
requireConsents: true, // `false` means "disable the feature"
defaultPurposes: nil // specify custom purposes for Piano products, `nil` means "use default mapping"
)
PianoConsents.initialize(configuration: configuration)
Purposes and products
Since consent v2 (PA JS 6.14.0), the consent is set by purpose instead of Piano products. Each product belongs to one purpose by default, by you can alter our default setup.
Default distribution:
Purpose | Products |
---|---|
Audience measurement (AM ) | Analytics (PA ) |
Content personalization and Performance (CP ) | Composer (COMPOSER ) |
Advertising (AD ) | DMP , Social Flow (SOCIAL_FLOW ) |
Personal Relationship (PR ) | ID , VX , ESP |
Data Layer (DL ) (JS only) | DL |
"Data Layer" purpose and the assigned DL product are reserved by Piano. Only the DL product can be assigned to the DL purpose.
Altering the Default Purposes
You can specify your own purposes if the ones provided by Piano don't match your consent banner. For example:
- Javascript
- Android (3.4.0+)
- Apple
window.pdl = window.pdl || {};
window.pdl.requireConsent = "v2";
window.pdl.consent = {};
window.pdl.consent.defaultPurposes = {
DMP: "Custom purpose 1",
COMPOSER: "Custom purpose 2",
};
val pianoConsents = PianoConsents.init(
context,
ConsentConfiguration(
requireConsent = true,
defaultPurposes = mapOf( // specify custom purposes for Piano products, `null` means "use default mapping"
Product.DMP to Purpose("Custom purpose 1"),
Product.COMPOSER to Purpose("Custom purpose 2"),
)
)
)
PianoAnalytics.init(applicationContext, configuration, pianoConsents)
let configuration = PianoConsentsConfiguration(
requireConsents: true, // `false` means "disable the feature"
defaultPurposes: [ // specify custom purposes for Piano products, `nil` means "use default mapping"
PianoConsentProduct.DMP: PianoConsentPurpose.init(custom: "Custom purpose 1"),
PianoConsentProduct.COMPOSER: PianoConsentPurpose.init(custom: "Custom purpose 2"),
]
)
PianoConsents.initialize(configuration: configuration)
In this example since Composer is moved to a different purpose the CP purpose doesn’t contain any product and doesn’t exist anymore.
Based on the above code, here is how the purposes and products would look like after if it's applied:
Purpose | Products |
---|---|
Audience measurement (AM ) | Analytics (PA ) |
Custom purpose 2 | Composer (COMPOSER ) |
Custom purpose 1 | DMP |
Advertising (AD ) | Social Flow (SOCIAL_FLOW ) |
Personal Relationship (PR ) | ID , VX , ESP |
Data Layer (DL ) | DL |
Set consent mode by purpose
The Consent Mode will be in charge of sending the right information, depending on what you have configured.
It will be stored in a _pprv
cookie along with other Piano products consent, and will be used on all events until it is modified, or until the cookie is deleted.
- Javascript
- Android (3.4.0+)
- Apple
- Flutter
Method:
pa.consent.setByPurpose(purpose, mode, product?)
Parameters | Type | Description |
---|---|---|
purpose | string | Purpose to set. AM , CP , AD , PR or DL |
mode | string | Consent mode. opt-in , essential or opt-out |
product | string / array | Product(s) to assign to the purpose. PA , COMPOSER , DMP , VX , ID , ESP |
pa.consent.setByPurpose("AM", "essential");
// edit AM products
pa.consent.setByPurpose("AM", "essential", ["PA", "COMPOSER"]);
You can also set a Consent mode for all purposes.
pa.consent.setAllPurposes(mode)
Parameters | Type | Description |
---|---|---|
mode | string | Consent mode. opt-in , essential or opt-out |
pa.consent.setAllPurposes("essential");
val pianoConsents = PianoConsents.getInstance() // or can be retrieved via PianoAnalytics.getInstance().pianoConsents (null if consents are not used)
pianoConsents.set(Purpose.AUDIENCE_MEASUREMENT, ConsentMode.ESSENTIAL)
// edit AM products
pianoConsents.set(Purpose.AUDIENCE_MEASUREMENT, ConsentMode.ESSENTIAL, Product.PA, Product.COMPOSER)
You can also set a Consent mode for all purposes.
PianoConsents.getInstance().setAll(ConsentMode.ESSENTIAL)
await _pianoConsents.set(
purpose: PianoConsentPurpose.audienceMeasurement,
mode: PianoConsentMode.essential,
products: [PianoConsentProduct.pa]);
You can also set a Consent mode for all purposes.
await _pianoConsents.setAll(mode: PianoConsentMode.essential);
PianoConsents.shared.set(
purpose: PianoConsentPurpose.AUDIENCE_MEASUREMENT,
mode: PianoConsentMode.ESSENTIAL,
)
// edit AM products
PianoConsents.shared.set(
purpose: PianoConsentPurpose.AUDIENCE_MEASUREMENT,
mode: PianoConsentMode.ESSENTIAL,
products: PianoConsentProduct.PA, PianoConsentProduct.COMPOSER
)
You can also set a Consent mode for all purposes.
PianoConsents.shared.setAll(mode: PianoConsentMode.ESSENTIAL)
Retrieve consent mode by purpose
If you want to know the current consent mode for each purposes, you can use the following method:
- Javascript
- Android (3.4.0+)
- Apple
Method:
pa.consent.getByPurpose()
pa.consent.getByPurpose();
// {
// AM: {mode: 'opt-in', products: ["PA"]},
// CP: {mode: 'opt-in', products: ["COMPOSER"]},
// AD: {mode: 'opt-in', products: ["DMP", "SOCIAL_FLOW"]},
// PR: {mode: 'opt-out', products: ["ID", "VX", "ESP"]}
// DL: {mode: 'opt-in', products: ["DL"]},
// }
PianoConsents.getInstance().consents
// returns Map<Purpose, Consent>, where
// - Purpose is one of [AUDIENCE_MEASUREMENT, CONTENT_PERSONALISATION, ADVERTISING, PERSONAL_RELATIONSHIP],
// - Consent is class Consent(val mode: ConsentMode, val products: List<Product>),
// - ConsentMode is one of [OPT_IN, ESSENTIAL, OPT_OUT, CUSTOM, NOT_ACQUIRED],
// - Product is one of [PA, DMP, COMPOSER, ID, VX, ESP, SOCIAL_FLOW]
PianoConsents.shared.consents
// returns Map<Purpose, Consent>, where
// - Purpose is one of [AUDIENCE_MEASUREMENT, CONTENT_PERSONALISATION, ADVERTISING, PERSONAL_RELATIONSHIP],
// - Consent is class Consent(val mode: ConsentMode, val products: List<Product>),
// - ConsentMode is one of [OPT_IN, ESSENTIAL, OPT_OUT, CUSTOM, NOT_ACQUIRED],
// - Product is one of [PA, DMP, COMPOSER, ID, VX, ESP, SOCIAL_FLOW]
Configure the default consent mode
- Javascript
- Android (3.4.0+)
- Apple
- Flutter
In order to define the default consent mode for Piano Analytics (PA
), you need to set the consent.defaultPreset.PA
variable to the desired value (optin
by default):
window.pdl = window.pdl || {};
window.pdl.requireConsent = "v2";
window.pdl.consent = {
defaultPreset: {
PA: "essential",
},
};
// If you use Analytics only
window.pdl.consent.products = ["PA"];
With Kotlin SDK, you need to set the consent mode after the consents init:
// call after PianoConsents.init(...)
PianoConsents.set(...)
final _pianoConsents = PianoConsents(
requireConsents: true,
defaultPurposes: {
PianoConsentProduct.pa: PianoConsentPurpose.audienceMeasurement
}
);
With iOS SDK, you need to set the consent mode after the consents init:
// call after PianoConsents.initialize(...)
PianoConsents.set(...)
Manage default Consent modes
As explained in the Key notion section, you can manage your default Consent Modes by controlling several items:
- properties / events
- the value your
visitor_privacy_mode
andvisitor_privacy_consent
properties will take
- Javascript
- Android (3.4.0+)
- Apple
- Flutter
For JS SDK, this is achieved by declaring mode items before loading the SDK, thanks to window.pdl.consent_items.PA
object.
You need to define the type of items you need to edit: properties
or events
.
You'll then need to define the priority level of each property/event you need to modify. Three levels exist: mandatory
(higher priority) > essential
> optional
(lower priority).
Priority level refers to the consent level required for the data to be sent/stored. You can imagine it as follow:
Any item with a higher priority will also be sent with lower priority modes. For example, an item with a "mandatory" priority will be included de facto in "essential" and "opt-in" modes.
On the other hand, if we try to avoid sending event_collection_platform
in "opt-out" and "essential" modes:
window.pdl.consent_items = {
PA: {
properties: {
event_collection_platform: "optional",
},
},
};
As event_collection_platform
has the lowest priority level ("optional"), it won't be included into "essential" and "opt-out" modes.
The window.pdl.consent_items.PA
object specifications:
Property | Description | Type | Value |
---|---|---|---|
properties | Properties priority configuration | object of consentItem | { consentItem } |
events | Events priority configuration | object of consentItem | { consentItem } |
The window.pdl.consent_items.PA.properties
,window.pdl.consent_items.PA.events
(consentItem
) objects specifications:
Property | Description | Type | Value |
---|---|---|---|
<key> | Key to configure, with the priority as the value | string | event_collection_platform |
Complete example:
window.pdl.consent_items = {
PA: {
properties: {
event_collection_platform: "essential",
event_collection_version: "optional",
},
events: {
"ad.view": "optional",
"ad.click": "essential",
},
},
};
With Kotlin SDK, can use the same methods as the Privacy implementation:
// allow Visitor storage for Exempt mode
PrivacyMode.EXEMPT.allowedStorageFeatures += PrivacyStorageFeature.VISITOR
// forbid Visitor storage for Exempt mode
PrivacyMode.EXEMPT.forbiddenStorageFeatures += PrivacyStorageFeature.VISITOR
// allow property `prop_name` for any event for Exempt mode
PrivacyMode.EXEMPT.allowedPropertyKeys[Event.ANY]?.add(PropertyName("prop_name"))
// forbid property `prop_name` for any event for Exempt mode
PrivacyMode.EXEMPT.forbiddenPropertyKeys[Event.ANY]?.add(PropertyName("prop_name"))
// allow event `selfpromotion.impression` for Exempt mode
PrivacyMode.EXEMPT.allowedEventNames.add("selfpromotion.impression")
// forbid event `selfpromotion.impression` for Exempt mode
PrivacyMode.EXEMPT.forbiddenEventNames.add("selfpromotion.impression")
With Kotlin SDK, can use the same methods as the Privacy implementation:
// allow Visitor storage for Exempt mode
await _pianoAnalytics.privacyIncludeStorageFeatures(
features: [PrivacyStorageFeature.visitor],
modes: [PrivacyMode.exempt]
);
// forbid Visitor storage for Exempt mode
await _pianoAnalytics.privacyExcludeStorageFeatures(
features: [PrivacyStorageFeature.visitor],
modes: [PrivacyMode.exempt]
);
// allow property `prop_name` for event "page.display" for Exempt mode
await _pianoAnalytics.privacyIncludeProperties(
propertyNames: ["prop_name"],
modes: [PrivacyMode.exempt],
eventNames: ["page.display"]
);
// forbid property `prop_name` for event "page.display" for Exempt mode
await _pianoAnalytics.privacyExcludeProperties(
propertyNames: ["prop_name"],
modes: [PrivacyMode.exempt],
eventNames: ["page.display"]
);
// allow event `selfpromotion.impression` for Exempt mode
await _pianoAnalytics.privacyIncludeEvents(
eventNames: ["selfpromotion.impression"],
modes: [PrivacyMode.exempt]
);
// forbid event `selfpromotion.impression` for Exempt mode
await _pianoAnalytics.privacyExcludeEvents(
eventNames: ["selfpromotion.impression"],
modes: [PrivacyMode.exempt]
);
With iOS SDK, can use the same methods as the Privacy implementation:
// allow Visitor storage for Exempt mode
pa.privacyIncludeStorageKey(PA.Privacy.Storge.Visitor, privacyModes: ["exempt"]);
// forbid Visitor storage for Exempt mode
pa.privacyExcludeStorageKey(PA.Privacy.Storge.Visitor, privacyModes: ["exempt"]);
// allow property `prop_name` for any event for Exempt mode
pa.privacyIncludeProperty("prop_name", privacyModes: ["exempt"]);
// forbid property `prop_name` for any event for Exempt mode
pa.privacyExcludeProperty("prop_name", privacyModes: ["exempt"]);
// allow event `selfpromotion.impression` for Exempt mode
pa.privacyIncludeEvent("selfpromotion.impression", privacyModes: ["exempt"]);
// forbid event `selfpromotion.impression` for Exempt mode
pa.privacyExcludeEvent("selfpromotion.impression", privacyModes: ["exempt"]);
Use product consent (deprecated)
Set a Consent Mode
The Consent Mode will be in charge of sending the right information, depending on what you have configured.
It will be stored in a _pprv
cookie along with other Piano products consent, and will be used on all events until it is modified, or until the cookie is deleted.
Method:
pa.consent.setMode(mode)
Parameters | Type | Description |
---|---|---|
mode | string | Privacy Mode. opt-in , essential or opt-out |
pa.consent.setMode("essential");
If you don't set any Consent Mode (and none stored), the value set for configuration window.pdl.consent.defaultPreset.PA
will be applied by default.
opt-in
by default.
Retrieve the Consent Mode
If you want to know the current mode used by the SDK, you can use the following method:
Method:
pa.consent.getMode()
pa.consent.getMode(); // Return a string with the Consent Mode value
Manage a custom Consent mode
- Javascript
- Android (3.4.0+)
- Apple
By default, the custom
mode uses the same rules as the opt-in
mode.
As explained in the Key notion section, you can manage your custom Consent Mode by controlling several items:
- properties / events
- the value your
visitor_privacy_mode
andvisitor_privacy_consent
properties will take
You first need to declare the "source mode" of your custom mode with window.pdl.consent_modifiers.PA.source
, with one of the default modes:
opt-in
essential
opt-out
Each rule is then a new entry in the window.pdl.consent_modifiers.PA.patches
array, with an action
and an item
.
Example trying to avoid sending event_collection_platform
in "opt-out" and "essential" modes:
window.pdl.consent_modifiers = {
PA: {
source: "essential",
patches: [
{
action: "exclude",
item: {
key: "event_collection_platform",
type: "property",
},
},
],
},
};
The property
named event_collection_platform
has been excluded from the custom mode.
The window.pdl.consent_modifiers.PA
object specifications:
Property | Description | Type | Value |
---|---|---|---|
source | Base mode for configuration of the custom mode | string | opt-in , essential or opt-out |
patches | Patches to edit configuration of the custom mode | array of modePatch | [ modePatches ] |
The window.pdl.consent_modifiers.PA.patches
(modePatch
) array specifications:
Property | Description | Type | Value |
---|---|---|---|
action | Action to perform | string | exclude or include |
item | Item to perform action on | patchItem | patchItem |
The window.pdl.consent_modifiers.PA.patches[].item
(patchItem
) object specifications:
Property | Description | Type | Value |
---|---|---|---|
key | Key of the item | string | page |
type | Type of the item | string | event or property |
Complete example:
window.pdl.consent_modifiers = {
PA: {
source: "optout",
patches: [
{
action: "exclude",
item: {
key: "event_collection_platform",
type: "property",
},
},
{
action: "exclude",
item: {
key: "ad.view",
type: "event",
},
},
],
},
};
You can also manage the custom mode name (sent in visitor_privacy_mode
) and consent type (sent in visitor_privacy_consent
) with:
Method:
pa.consent.setCustomModeMetadata(modeConsent, modeName)
Parameters | Type | Description |
---|---|---|
modeConsent | boolean | Custom Consent mode consent status |
modeName | string | Custom Consent mode name |
pa.consent.setMode("custom");
pa.consent.setCustomModeMetadata(false, "myCustomMode");
Don't forget to set the mode to custom
to apply the changes
In order to set a Custom mode (will be named Custom
), you can proceed as for default modes:
PianoConsents.getInstance().set(Purpose.ESSENTIAL, ConsentMode.CUSTOM)
In order to set a Custom mode (will be named Custom
), you can proceed as for default modes:
PianoConsents.shared.set(
purpose: PianoConsentPurpose.ESSENTIAL,
mode: PianoConsentMode.Custom
)
How to migrate
- Android (3.4.0+)
In order to migrate from Privacy implementation, here are some steps to follow or check:
-
Add initialization for consents
val pianoConsents = PianoConsents.init(
applicationContext,
ConsentConfiguration(
requireConsent = true,
defaultPurposes = mapOf(...) // if needed
)
) -
Add consents to Piano Analytics initialization
PianoAnalytics.init(applicationContext, configuration, pianoConsents)
-
Set default preset (if needed)
pianoConsents.set(...)
-
Configure privacy modes (if needed)
PrivacyMode.EXEMPT.allowedEventNames.add("event-name-here")
-
Configure custom privacy mode (if needed)
PrivacyMode.CUSTOM.apply { ... }
-
Remove all usages of
PianoAnalytics.getInstance().privacyModesStorage.allModes
-
Replace setting
PianoAnalytics.getInstance().privacyModesStorage.currentMode
viapianoConsents.set(...)
, use this mapping:PrivacyMode.OPTIN -> ConsentMode.OPT_IN
PrivacyMode.EXEMPT -> ConsentMode.ESSENTIAL
PrivacyMode.OPTOUT -> ConsentMode.OPT_OUT
PrivacyMode.CUSTOM -> ConsentMode.CUSTOMNote: default value "consent not acquired" will be used if you haven't set any other value
-
Purpose values are (JS name in brackets):
Purpose.AUDIENCE_MEASUREMENT (AM)
Purpose.CONTENT_PERSONALISATION (CP)
Purpose.ADVERTISING (AD)
Purpose.PERSONAL_RELATIONSHIP (PR)