Consent in apps

You choose how consent works in your app, per app and in code. X3 gives you three modes, four separate purposes, a consent sheet for the first launch and a privacy settings screen, and it enforces your choice on the device and on the server. There is no hidden default: consent.mode is a required option of configure, so the decision is always a conscious one.

You decide how consent works in your app, and you are responsible for that decision. X3 implements your choice technically but does not check whether it is legally right for your users and countries. Clarify the legal basis with your privacy counsel and describe X3 in your privacy policy and in the App Store and Google Play privacy details.

This page explains the technical behaviour and lists typical obligations. It is not legal advice.

The three consent modesAlways on sends from the first launch and stops only when the user opts out in your settings. Opt-in sends nothing until the user grants usage analytics in your first-launch dialog; a denial keeps it silent. Opt-out sends from the first launch and stops when the user switches measurement off in your settings. In every mode an opt-out deletes the queue, the install id and the identity, and X3.lead() always works because it is an explicit inquiry.Always onmode: alwaysOpt-inmode: opt_inOpt-outmode: opt_outFirst launchUser grantsUser denies or opts outSends everythingyou enabled, fromthe first launchNo dialog needed;your legal basiscovers itsetOptOut(true) fromyour settings: stops,deletes queue and idsSends nothing;purposes pending(optional memory buffer)Starts now: installid, session, config;buffered events sentStays silent;buffer dropped,nothing on diskSends everythingyou enabled, fromthe first launchNo change;explicit grantsare rememberedSwitch in yoursettings: stops, deletesqueue and idsEvery mode: per purpose switches for analytics, attribution, recordings and adPlatforms · X3.lead() always worksHealth projects: recordings and ad platform uploads only from explicit opt-in grants · you choose the mode and you are responsible for it
The three consent modes
ModeDashboard labelBefore a decisionAfter a grantAfter a denial or opt-out
alwaysImmer an (Always on)sends everything your app enabled, from the first launchno changeonly setOptOut(true) (for example the switch in your settings) or a purpose set to false stops it
opt_inOpt-insends nothing: no config request, no batch, no install request, no recording; the purposes are pendingthe granted purposes start at once (install id, session, config)nothing is sent; with bufferUntilDecision the in-memory buffer is dropped
opt_outOpt-outsends everything your app enabled, from the first launchno change; explicit choices are rememberedthe denied purposes stop at once; an opt-out stops everything and deletes the local data

Choose the mode in configure:

X3.configure(key: "pk_3x_YOUR_APP_KEY", options: X3Options(consent: X3ConsentOptions(mode: .optIn)))
X3.configure(context, "pk_3x_YOUR_APP_KEY", X3Options(consent = X3ConsentOptions(mode = X3ConsentMode.OPT_OUT)))
X3.configure("pk_3x_YOUR_APP_KEY", { consent: { mode: "always", purposes: { adPlatforms: true } } });
await X3.configure('pk_3x_YOUR_APP_KEY', const X3Options(consent: X3ConsentOptions(mode: X3ConsentMode.optIn)));

Typical choices (your counsel decides): opt_in for apps with users in the EU when you rely on consent; opt_out where your legal basis allows measurement by default with an easy way out; always when you have another legal basis (for example outside the EU, or an internal company app) and still offer an opt-out in your settings. The dashboard shows the mode each app reports (Einstellungen › Apps), so you can check every release.

Buffer until a decision. In opt_in mode the option bufferUntilDecision: true keeps this process's events in memory (at most 200, never on disk) until the user decides: granted, they are sent; denied, they are dropped. Without it, everything before the decision is lost on purpose.

Changing the mode between app versions. Persisted decisions keep their meaning: a user who granted analytics in an opt_in version stays granted after you switch to opt_out, and a user who opted out stays opted out.

Purposes

Purpose (code)Dashboard and sheet label (DE / EN)CoversEnforced
analyticsNutzung messen / Usage analyticsinstall id, sessions, screens, events, identify, the offline queue, config requestson the device: nothing is sent without it; on the server: batches without it are not stored
attributionHerkunft der Installation / Install attributionreading the Play Install Referrer and the Apple Ads token, SKAdNetwork and AdAttributionKit conversion value updates, deep link attributionon the device; the server stores installs without it with tier unknown
recordingsSitzungen aufzeichnen / Session recordingswireframe and screenshot recordingson the device (recorder off) and the server (packets refused)
adPlatformsWerbung messen / Ad measurementreporting conversions of this person to Meta, Google Ads, TikTok and LinkedInon the server: no upload for the session without it

purposes in configure says which purposes your app uses at all. Defaults: analytics and attribution on, recordings and adPlatforms off. A purpose that is not used is never asked for and never active. analytics is the base: without it nothing else is sent either.

Each batch carries the consent mode and a four-letter consent string in the order analytics, attribution, recordings, adPlatforms, with g granted, d denied, p pending and x not used: gdxx means analytics granted, attribution denied, recordings and ad platforms not used.

State rules

The effective state of a used purpose:

SituationState
the user opted out (setOptOut(true))denied
mode always or opt_out, and the user did not deny this purposegranted
mode always or opt_out, and the user denied this purpose (consent(…: false))denied
mode opt_in, the user granted this purposegranted
mode opt_in, the user denied this purposedenied
mode opt_in, no decision yetpending

X3.consentState() returns the mode, the four states, optedOut and needsDecision (mode opt_in, some used purpose still pending, not opted out). Show your consent screen when needsDecision is true.

Decisions are stored in consent.json in the SDK's private directory, the only file the SDK writes while analytics is not granted. Only explicit decisions are stored; a later version of your app reads them back.

Transitions

ChangeWhat happens at once
any purpose grantedthat purpose starts; the first grant of analytics creates the install id, the device secret and the session
analytics deniedeverything stops; the queue (memory and disk), install id, device secret, identity, account token and cached config are deleted; nothing more is sent. A later grant starts a new install (new install id, the original install source is lost)
attribution deniedno more attribution reads or conversion value updates; an install request not sent yet is never sent
recordings deniedthe recorder stops and sends one revoke packet: X3 deletes this session's recordings
adPlatforms deniedthe next batches say so; X3 stops uploads for this session
setOptOut(true)all used purposes denied, with the effects above
setOptOut(false)back to the mode's states; explicit decisions are kept
X3.consent(X3ConsentUpdate(recordings: true, adPlatforms: false))   // per purpose
X3.consentAll(true)                                                 // every used purpose
X3.setOptOut(true)                                                  // the "Allow measurement" switch off
let state = X3.consentState()
let subscription = X3.onConsentChange { state in }                  // main queue; subscription.cancel()

Kotlin X3.consent(X3ConsentUpdate(recordings = true)), X3.addConsentListener { state -> }; React Native X3.onConsentChange(listener) returns an unsubscribe function; Flutter X3.consentChanges is a stream.

Consent management platforms. X3 has no adapters for mobile CMPs. Call X3.consent(…) from your CMP's callback (Usercentrics, OneTrust, Didomi and others), mapping your CMP's categories to the four purposes, and set the mode to opt_in.

The helpers never show themselves; you decide when. They follow the device language (de* → German, otherwise English), support Dynamic Type and font scaling, VoiceOver and TalkBack, 44 pt / 48 dp touch targets and light and dark appearance.

HelperiOS (X3KitUI)AndroidReact NativeFlutter
consent sheetX3ConsentView(texts:onDone:), X3.presentConsent(from:texts:completion:)X3ConsentDialog.show(activity, texts) { state -> }, Compose X3ConsentSheet(...)<X3ConsentSheet visible onDone texts />X3ConsentSheet.show(context, texts: …)
settingsX3PrivacySettingsView(texts:), X3PrivacySettingsViewControllerX3PrivacySettingsDialog.show(activity, texts), Compose X3PrivacySettings(...)<X3PrivacySettings texts />X3PrivacySettings(texts: …)

Consent sheet. Lists the used purposes (usage analytics first, then install attribution, session recordings, ad measurement) with a switch each (on for granted, off otherwise), a link to your privacy policy when you set its URL, and three equally prominent buttons: Alle akzeptieren (grant all used purposes), Ablehnen (deny all used purposes), Auswahl speichern (apply the switches).

Settings. The same switches, applied immediately. In mode always it shows an info line and one switch Messung erlauben (Allow measurement) bound to setOptOut.

Default texts (override any of them with X3ConsentTexts; {app} is the app's display name or appName):

KeyDEEN
titleHilf uns, {app} besser zu machenHelp us improve {app}
bodyWir möchten verstehen, wie Du {app} nutzt. Du entscheidest, was erlaubt ist, und kannst Deine Wahl jederzeit in den Einstellungen ändern.We would like to understand how you use {app}. You decide what is allowed and can change your choice in the settings at any time.
analytics.titleNutzung messenUsage analytics
analytics.textWelche Bildschirme Du öffnest und welche Funktionen Du nutzt. Hilft uns, die App zu verbessern.Which screens you open and which features you use. Helps us improve the app.
attribution.titleHerkunft der InstallationInstall attribution
attribution.textÜber welche Anzeige oder welchen Link Du zu uns gekommen bist.Which ad or link brought you to us.
recordings.titleSitzungen aufzeichnenSession recordings
recordings.textEine vereinfachte Aufzeichnung Deiner Bildschirme. Eingaben werden nie aufgezeichnet.A simplified recording of your screens. What you type is never recorded.
adPlatforms.titleWerbung messenAd measurement
adPlatforms.textWir melden Anmeldungen und Käufe an Werbepartner wie Meta oder Google, um unsere Werbung zu verbessern.We report sign-ups and purchases to ad partners such as Meta or Google to improve our ads.
acceptAllAlle akzeptierenAccept all
rejectAllAblehnenReject
saveAuswahl speichernSave choice
privacyPolicyDatenschutzerklärungPrivacy policy
settings.titleDatenschutzPrivacy
settings.alwaysInfoDiese App misst die Nutzung, um sie zu verbessern.This app measures usage to improve it.
settings.allowMessung erlaubenAllow measurement
indicatorAufnahmeRecording

If you change texts, keep them accurate: they must describe what your app really does with each purpose, and Ablehnen must stay as easy to reach as Alle akzeptieren.

Your obligations

Whatever mode you choose, you are the controller of your users' data and X3 is your processor. You are responsible for the legal basis and for what you tell your users and the stores. Typical obligations (check them with your privacy counsel):

  • Privacy policy. Name X3 Growth Intelligence (good vibes GmbH, Gartenstrasse 7, c/o excent AG, Zweigniederlassung Zug, 6300 Zug, Switzerland) as your processor, the purposes you use (usage analytics, install attribution, session recordings, ad measurement), what is collected (install id, screens and events, hashed user id and e-mail, purchases, campaign of the install, coarse location derived from the IP address), the ad platforms you report to, retention and the users' rights. The privacy policy template for websites is a starting point.
  • App Store privacy details. Declare the data types of the modules you use in App Store Connect (table). Apple counts hashed e-mail addresses as e-mail addresses.
  • App Tracking Transparency. When iOS conversions with user data (hashed e-mail, user id) go to ad platforms, Apple treats that as tracking: ask for ATT permission, declare tracking in App Store Connect, and set adPlatforms accordingly. X3 sends iOS user data to ad platforms only with ATT authorized (ATT rule).
  • Google Play data safety. Fill in the data safety form with the Play rows for your modules.
  • GDPR legal basis. Processing needs a legal basis (Art. 6 GDPR): consent (Art. 6(1)(a)) or, only after your own documented assessment, legitimate interest (Art. 6(1)(f)). Recordings and reporting to ad platforms are typically consent cases.
  • Device access (§ 25 TDDDG, formerly TTDSG, and Art. 5(3) ePrivacy). Storing the install id on the device and reading device information (install referrer, Apple Ads token, ATT status) is access to the terminal equipment in the sense of § 25 TDDDG and Art. 5(3) of the ePrivacy Directive; the EDPB guidelines 2/2023 on its technical scope apply to apps as much as to browsers. Unless an exception applies, this needs consent; opt_in is the mode for that.
  • Data subject rights. Answer access and deletion requests. Delete a user's app data with POST /v1/projects/{project}/app-users/erase (by user id, e-mail or install id); Apple also requires account deletion inside apps that offer accounts.
  • Data processing agreement. Conclude the DPA (Art. 28 GDPR) with X3 before you go live (processor and DPA).
  • Recordings. Apple's App Review Guideline 2.5.14 requires explicit consent and a clear visual indication when an app records user activity; keep the recording indicator on.

X3 makes no promise of legal compliance on your behalf, and your texts should not either without your counsel's review.

You decide how consent works in your app, and you are responsible for that decision. X3 implements your choice technically but does not check whether it is legally right for your users and countries. Clarify the legal basis with your privacy counsel and describe X3 in your privacy policy and in the App Store and Google Play privacy details.

Health projects

Projects with the health preset keep stricter rules regardless of the mode you choose:

  • recordings are accepted only from an explicit opt-in grant: an app in mode always or opt_out records nothing in a health project, and the server refuses its packets;
  • conversions are uploaded to ad platforms only from explicit opt-in grants of adPlatforms;
  • the health preset's recording rules apply as on the web: masking relaxations are locked, more screens are excluded and recordings cannot be shared by link (recordings);
  • health data needs explicit consent (Art. 9 GDPR): use opt_in for every purpose.

What the server checks

RequestRule
batchesstored only when analytics is granted; otherwise answered stored: false with reason consent
install requestwithout attribution granted the install is stored without sources (tier unknown)
recording packetsneed analytics and recordings granted; in health projects also mode opt_in
ad platform uploadsneed adPlatforms granted in the session; iOS user data only with ATT authorized; health projects only from opt-in grants