Mobile apps (preview)
Fetch your published banner configuration in a native app and record consent from the mobile SDKs.
The native mobile SDKs are in preview. This page describes the server contract every SDK uses, so you can also build against it directly.
A native app uses the same banner as your website: the same categories, texts, regional rules and consent revision. Publish the banner (or an environment) in the dashboard first; apps only ever receive published configuration, never drafts.
Fetch the configuration
GET /api/v1/mobile/config?bannerId=PUBLIC_BANNER_ID&env=production&lang=de
| Parameter | Required | Notes |
|---|---|---|
bannerId | Yes | The public Banner ID, the one in the loader script URL |
env | No | Environment slug; defaults to production. Other environments return their own published version |
lang | No | Preferred language tag such as de or pt-BR. Without it the Accept-Language header is used |
No API key is needed: the response contains only what the published banner script already makes public. The endpoint is rate limited per client IP like the CDN routes.
The visitor's region is taken from the request's edge geo headers, exactly as for the website, and the response carries the resolved regional outcome. Because of that the response is marked private: it may be cached by the app, never by a shared proxy. Send the ETag back in If-None-Match to get 304 Not Modified when nothing changed.
An unknown banner, an inactive banner, an unknown environment and a banner or environment that has never been published all return the same 404 with the code not_found. A malformed query returns 400 with the code invalid_request.
Response
The body is a versioned JSON document. schemaVersion is 1; new fields can be added without changing it, and a breaking change would ship as a new schema version alongside a new SDK major version.
| Field | Contents |
|---|---|
banner.publicId, banner.id | Public Banner ID, and the internal ID that POST /api/v1/consent/log expects as bannerId |
environment | The environment slug and whether it is production |
version | Published version number, publishedAt, a configHash of the consent-relevant settings, and consentRevision |
consent.expiresAfterDays | The banner's consent lifetime |
language | The requested tag, the resolved language of the texts, the banner default and every available language |
texts | Banner and preferences screen copy: title, description, accept, reject, preferences, save and close labels |
categories | Per category: id, required, defaultEnabled, name, description and the Google Consent Mode v2 keys it grants |
consentMode | Whether Consent Mode v2 is enabled and the region-aware defaults to apply before anything else |
regional | Country and region, matched rule and framework, consentModel (opt-in, opt-out or notice), showBanner, the categories in effect before a choice (defaultGrantedCategories), a regional consent lifetime and US privacy flags |
links | Privacy and cookie policy URLs |
Language resolution follows the website: the banner's own default and customized translations come first, then the platform translation for the requested language, then the banner's default language, then English. Descriptions can contain the same limited HTML (links, bold, italics, line breaks) as on the website.
When to ask again
Store the choice on the device together with version.consentRevision, the regional.ruleId it was made under and its timestamp. Show the banner again when there is no stored choice, when consentRevision changed (the same rule the website applies to its consent cookie), when the matched regional rule changed, or when the choice is older than regional.consentExpiryDays (or consent.expiresAfterDays when the region sets none). When regional.showBanner is false, store defaultGrantedCategories without asking.
Record consent
Post each explicit choice to the versioned consent log with an API key that has the consent:write scope. See Authentication.
A key compiled into an app can be extracted from it. Create a dedicated key with only the
consent:writescope for the app so it can be revoked on its own, or relay consent records through your own backend and keep the key there.
POST /api/v1/consent/log
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"bannerId": "INTERNAL_BANNER_ID_FROM_banner.id",
"consentChoices": { "necessary": true, "analytics": true, "marketing": false },
"language": "de",
"timestamp": "2025-01-15T10:00:00.000Z",
"platform": "android",
"sdkVersion": "0.1.0"
}
platform is one of web, ios, android, react-native or flutter, and sdkVersion is a short version string. Both are optional and stored with the record, so consent analytics can be split by platform. Records written without them keep a null platform.
Android
The Android core library (com.cookiebeam:consent, preview, not yet published to Maven Central) implements this contract: it fetches and caches the configuration, decides when to ask, stores the choice, posts it with platform: "android" and maps it to Consent Mode v2 for Firebase Analytics. It is headless; your app renders the texts and categories.
val consent = CookieBeamConsent.create(
options = CookieBeamOptions(publicBannerId = "PUBLIC_BANNER_ID"),
storage = yourEncryptedStorage,
apiKey = BuildConfig.COOKIEBEAM_CONSENT_KEY,
)
consent.addListener { effective -> applyConsent(effective) }
// On a background thread:
if (consent.refresh().decision.needsBanner) showConsentScreen(consent.config!!)
// From your consent screen:
consent.acceptAll() // or consent.rejectAll(), consent.save(mapOf("analytics" to true))
A ready-made banner UI for Compose and Views, encrypted storage and the Firebase bridge are the next Android deliverables. iOS, React Native and Flutter SDKs follow the same contract.