GDPR & CCPA
Keep in mind that it’s best to contact qualified legal professionals, if you haven’t done so already, to get more information and be well-prepared for compliance.
The General Data Protection Regulation, better known as GDPR, took effect on May 25, 2018. It’s a set of rules designed to give EU citizens more control over their personal data. Any businesses established in the EU or with users based in Europe are required to comply with GDPR or risk facing heavy fines. The California Consumer Privacy Act (CCPA) went into effect on January 1, 2020. We have put together some guidelines to help publishers understand better the steps they need to take to be GDPR compliant.
Step 1. Update Privacy Policy
Include Additional Information To Your Privacy Policy
Don’t forget to add information about IP address and advertising ID collection, as well as the link to Appodeal’s privacy policy to your app’s privacy policy on the App Store.
To speed up the process, you could use privacy policy generators - just insert advertising ID, IP address, and location (if you collect users’ location) in the Personally Identifiable Information you collect field (in line with other information about your app) and the link to Appodeal’s privacy policy in the Link to the privacy policy of third party service providers used by the app field.
Add A Privacy Policy To Your Mobile App
You must add your explicit privacy policies in two places: on your app’s Store Listing page and within your app.
You can find detailed instructions on adding your privacy policy to your app on legal service websites. For example, Iubenda, the solution tailored to legal compliance, provides a comprehensive guide on including a privacy policy in your app.
Make sure that your privacy policy website has an SSL certificate—this point might seem obvious, but it’s still essential.
Here are two useful resources that you can utilize while working on your app compliance:
- Privacy, Security and Deception regulations (by Google Play)
- Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)
Please note that although we’re always eager to back you up with valuable information, we’re not authorized to provide any legal advice. It’s important to address your questions to lawyers who specialize in this area.
Step 2. Configure Stack Consent Manager with TCF v2 Support
Since Appodeal SDK 3.2.1 it is fully compatible with Google UMP and supports IAB TCF v2.
In order for Appodeal and our ad providers to deliver ads that are more relevant to your users, as a mobile app publisher, you need to collect explicit user consent in the regions covered by GDPR.
To get consent for collecting personal data of your users, we suggest you use a ready-made solution - Stack Consent Manager based on Google User Messaging Platform (UMP).
Before you start, you need to configure Google UMP. Follow this instruction to setup a consent form.
Step 3. Integrate Stack Consent Manager
Stack Consent Manager comes with a pre-made consent window that you can easily present to your users. That means you no longer need to create your own consent window.
Consent will be requested automatically on SDK initialization, and consent form will be shown if it is necessary without any additional calls.
Please keep in mind that Consent will be shown only in the EU region, you can use VPN for testing.
This means that Appodeal SDK integration code remains the same:
- UPM Distribution
- Manual Distribution
private void Start()
{
int adTypes = AppodealAdType.Interstitial | AppodealAdType.Banner | AppodealAdType.RewardedVideo | AppodealAdType.Mrec;
string appKey = "YOUR_APPODEAL_APP_KEY";
AppodealCallbacks.Sdk.OnInitialized += OnInitializationFinished;
Appodeal.Initialize(appKey, adTypes);
}
#region Initialization Callback
public void OnInitializationFinished(object sender, SdkInitializedEventArgs e) { }
#endregion
class Test : IAppodealInitializationListener
{
private void Start()
{
int adTypes = Appodeal.INTERSTITIAL | Appodeal.BANNER | Appodeal.REWARDED_VIDEO | Appodeal.MREC;
string appKey = "YOUR_APPODEAL_APP_KEY";
Appodeal.initialize(appKey, adTypes, this);
}
#region Initialization Callback
public void onInitializationFinished(List<-string-> errors) { }
#endregion
}
Advanced
If you wish, you can manage and update consent manually using Appodeal CMP Unity Plugin. To do so, first you need to install the plugin as shown below:
-
Make sure you have Appodeal Unity Plugin v4.4.0 or newer installed via UPM.
-
Copy the link below, head to the Window → Package Manager → "+" → Add package from git URL, paste the copied link there and press enter.
https://github.com/appodeal/cmp-unity-plugin.git#v2.2.0
- Import the namespace
using AppodealStack.Cmp;
Update Consent Status
To update the consent, call the method:
private void Start()
{
ConsentManager.Instance.OnConsentInfoUpdateFailed += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentInfoUpdateFailed event triggered. Cause: {args.Cause}");
};
ConsentManager.Instance.OnConsentInfoUpdateSucceeded += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentInfoUpdateSucceeded event triggered.");
};
var parameters = new ConsentInfoParameters
{
AppKey = "YOUR_APPODEAL_APP_KEY",
IsUnderAgeToConsent = false,
Sdk = "Appodeal",
SdkVersion = Appodeal.GetNativeSDKVersion()
};
ConsentManager.Instance.RequestConsentInfoUpdate(parameters);
}
RequestConsentInfoUpdate method can be requested at any moment of the application lifecycle. We recommend call
request it at the application launch. Multiple request calls are allowed.
Required parameters: YOUR_APPODEAL_APP_KEY - Appodeal app key, you can get
it in your personal account;
ConsentInfoParameters - Data class representing the parameters for a consent update request in the Appodeal
Consent Manager. Use this class to encapsulate the necessary information for updating consent preferences.
Params:
AppKey- The key associated with the user for whom the consent is being updated.IsUnderAgeToConsent- Optional. Indicates whether the user is tagged for under the age of consent. Set to true if the user is under the age of consent, otherwise set to false or null.Sdk- Optional. The identifier for the SDK making the consent update request.SdkVersion- Optional. The version of the SDK making the consent update request.
ConsentManager.Instance.OnConsentInfoUpdateFailed, ConsentManager.Instance.OnConsentInfoUpdateSucceeded -
listeners for result request.
Current Consent Status
After consent info was updated you can check the current consent status:
var status = ConsentManager.Instance.ConsentStatus;
Enum class representing the possible consent statuses in the Appodeal Consent Manager.
Unknown- Represents an unknown consent status;Required- Represents a required consent status;NotRequired- Represents a not required consent status;Obtained- Represents an obtained consent status.
Load Consent Form
You can load and receive ConsentForm object using following code:
private ConsentForm consentForm;
ConsentManager.Instance.OnConsentFormLoadFailed += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentFormLoadFailed event triggered. Cause: {args.Cause}");
};
ConsentManager.Instance.OnConsentFormLoadSucceeded += (sender, args) =>
{
Debug.Log($"[Appodeal CMP] OnConsentFormLoadSucceeded event triggered.");
consentForm = args.ConsentForm;
};
ConsentManager.Instance.Load();
Show Consent Form
After the consent window is ready you can show it.
ConsentManager.Instance.OnConsentFormDismissed += (sender, args) =>
{
string message = "[Appodeal CMP] OnConsentFormDismissed event triggered.";
if (args.Error != null) message += $" Error: {args.Error}";
Debug.Log(message);
consentForm = null;
};
consentForm?.Show();
Load And Show If Required
Alternatively, you can load the form and show it immediately if required.
ConsentManager.Instance.OnConsentFormDismissed += (sender, args) =>
{
string message = "[Appodeal CMP] OnConsentFormDismissed event triggered.";
if (args.Error != null) message += $" Error: {args.Error}";
Debug.Log(message);
};
ConsentManager.Instance.LoadAndShowConsentFormIfRequired();
Revoke Consent
You can reset the consent status to Unknown, using method:
ConsentManager.Instance.Revoke();
US State Regulations Support (Privacy Entry Point)
2.1.0.US state privacy laws (CCPA, CPA, VCDPA, and others) follow an opt-out model: data processing is allowed by default, but users must be given a permanent way to opt out — typically a "Do Not Sell or Share My Personal Information" button (the Privacy Entry Point). In the US zone the consent form is not shown automatically on SDK initialization, because consent is not required at launch — the opt-out form must be shown on demand, in response to a user tap.
To support this, the Consent Manager exposes two members:
ConsentManager.Instance.PrivacyOptionsStatus— tells you whether you must surface a Privacy Entry Point button in your app UI.ConsentManager.Instance.ShowPrivacyOptionsForm()— shows the US opt-out form (or the GDPR re-consent form when called in the EEA).
Both become available after RequestConsentInfoUpdate completes.
Check whether a Privacy Entry Point is required
Use PrivacyOptionsStatus to decide whether to render the opt-out button. It returns
PrivacyOptionsStatus.Required for users in regulated US states and in the EEA (for GDPR re-consent),
PrivacyOptionsStatus.NotRequired elsewhere, and PrivacyOptionsStatus.Unknown before
RequestConsentInfoUpdate has completed.
if (ConsentManager.Instance.PrivacyOptionsStatus == PrivacyOptionsStatus.Required)
{
// Show a "Do Not Sell or Share My Personal Information" / Privacy Settings button
}
Show the Privacy Options form
Call ShowPrivacyOptionsForm from the click handler of your Privacy Entry Point button. This is the
only way to display the US opt-out form, and it must be triggered by an explicit user interaction —
not on SDK initialization. The result is delivered through the same OnConsentFormDismissed event used
by the consent form.
ConsentManager.Instance.OnConsentFormDismissed += (sender, args) =>
{
string message = "[Appodeal CMP] OnConsentFormDismissed event triggered.";
if (args.Error != null) message += $" Error: {args.Error}";
Debug.Log(message);
};
ConsentManager.Instance.ShowPrivacyOptionsForm();
Once the user interacts with the US opt-out form, Stack Consent Manager writes the corresponding
privacy keys (IABGPP_*) to the platform's default preferences, where ad networks read them. Before
the form has been shown at least once, these keys remain empty and ad networks may treat the user as
"no consent collected".
Non-Personalized Advertising
Consent is enforced automatically — no extra integration is required.
If you want to request non-personalized advertising regardless of the resolved consent, call
Appodeal.SetNonPersonalized(true). This disables the collection of data used for ad personalization,
and a publisher-set value takes precedence over the consent resolved from the CMP.
Call it before Appodeal.Initialize(...).
This is relevant in several scenarios:
- Age-restricted users (US). US state laws (CCPA/CPRA in California, and similar laws in Virginia,
Colorado, Connecticut, and others) restrict selling or sharing the personal data of minors, and COPPA
adds stricter rules for children under 13. Use this flag alongside
SetChildDirectedTreatmentwhen you cannot determine the exact age but targeting must be limited. - Users who declined personalized advertising through your own consent flow, when they are not subject to a specific regulation covered by the other APIs.
- General opt-out — a catch-all to suppress targeting signals when none of the more specific privacy flags apply.
Appodeal.SetNonPersonalized(true);
Debug Geography Override (Testing)
2.2.0 (Appodeal Unity Plugin 4.4.0).Which form the user sees depends on their geography: the GDPR consent form in the EEA, the US opt-out form in regulated US states, and no form elsewhere. To test each flow without a VPN, tell Stack Consent Manager which geography to simulate on your test devices.
Call ConsentManager.Instance.SetDebugSettings before RequestConsentInfoUpdate (or before
Appodeal.Initialize when you rely on the automatic flow):
Geography—ConsentDebugGeography.Eea,ConsentDebugGeography.RegulatedUsStateorConsentDebugGeography.Other.TestDeviceIds— ids of the physical devices the override is allowed on. Emulators and simulators are always allowed, so the list can stay empty there.
The native SDKs apply the override only when both conditions hold; otherwise it is ignored and the device log tells you why:
- the build is debuggable: a Unity Development Build on Android, an app run from Xcode on iOS.
Release builds log
[Appodeal CMP] Debug settings ignored - release build; - the device is an emulator/simulator or its id is listed in
TestDeviceIds. An unlisted device logs[Appodeal CMP] Debug settings ignored - this device is not registered. Add testDeviceIds = ["<id>"] to ConsentDebugSettings.— copy the id from that line. On Android it is stable across reinstalls; on iOS it is derived fromidentifierForVendorand changes when the last app from your vendor is deleted from the device.
When the override is applied, the log shows [Appodeal CMP] CMP=Stack - debug geography EEA sent to server,
and the consent status and form are resolved for the simulated geography. The same setting also drives
Google UMP when it is configured for the app. The Unity Editor ignores debug settings and logs that.
ConsentManager.Instance.SetDebugSettings(new ConsentDebugSettings
{
Geography = ConsentDebugGeography.Eea,
TestDeviceIds = new List<string> { "TEST_DEVICE_ID_FROM_LOG" }
});
ConsentManager.Instance.RequestConsentInfoUpdate(parameters);
Call SetDebugSettings(null) to remove the override.
A stored consent takes precedence over the simulated geography: once the user has answered the form, it
is not shown again until the consent is revoked (ConsentManager.Instance.Revoke()) or the app data is
cleared. Remove the debug settings before publishing; they are ignored in release builds anyway.