Age-Based Consent
Overview
Age-Based Consent in the Android and Android TV SDKs enables automatic consent enforcement based on a user’s age range.
Administrators configure age range groups in the OneTrust admin console and associate them with specific purposes or purpose groups. The SDK uses this configuration, along with the age range provided by your application, to enforce consent behavior at runtime.
When a user falls within a restricted age range:
- Consent for associated purposes is automatically denied
- The corresponding purpose toggles in the Preference Center are hidden
This ensures that age-based restrictions are consistently applied without requiring additional UI logic in the app.
How It Works
At a high level:
- Your app provides the user's age range
- The SDK sends this data to the OneTrust server
- The server determines consent restrictions based on your settings in the admin console
- The SDK enforces those restrictions in the UI and APIs
What's Changing
SDK Updates
- New delegate method to provide the user's age range
- New data model:
ProfileAgeRange(lower and upper bounds) - Automatic consent restriction based on age overlap
- Age range changes are logged to the server (
AGEGATE_RANGEinteraction)
Server (CMP API) Updates
- Accepts and stores user age range data
- Enforces restrictions by setting
consentToggleStatus = -1for restricted purposes - Maintains enforcement across devices for the same user profile
Implementation
1. Data Model: ProfileAgeRange
ProfileAgeRangeThis represent a user's age range
| Property | Description |
|---|---|
lowerBound | Optional minimum age |
upperBound | Optional maximum age |
lowerBound: Int? // -> Optional. Omit or pass null for "from 0".
upperBound: Int? // -> Optional. Omit or pass null for "no upper limit".
//NOTE: Android validates at init: bounds must be >= 0, and
//lowerBound must be <= upperBound when both are set.Sample Code
ProfileAgeRange(lowerBound = 13, upperBound = 17) // 13-17
ProfileAgeRange(lowerBound = 18, upperBound = null) // 18+
ProfileAgeRange(lowerBound = null, upperBound = 12) // 0-12At least one bound is required. Both values cannot be
nil.
2. Interface: getProfileAgeRange
getProfileAgeRangeRegister this optional interface in your appropriate interface before or immediately after startSDK
The SDK calls it automatically:
- During
startSDK - When
checkAndLogConsentis called
Sample code
val profileAgeRangeInterface = object : ProfileAgeRangeInterface {
override fun getProfileAgeRange(
profileId: String,
callback: ProfileAgeRangeCallback,
) {
if (profileId.equals("test1", ignoreCase = true)) {
callback.completion(ProfileAgeRange(0, 4))
} else if (profileId.equals("test2", ignoreCase = true)) {
callback.completion(ProfileAgeRange(9, 10))
} else {
callback.completion(ProfileAgeRange(null))
}
}
}
ProfileAgeRangeInterface profileAgeRangeInterface = new ProfileAgeRangeInterface() {
@Override
public void getProfileAgeRange(@NotNull String profileId, @NotNull ProfileAgeRangeCallback callback) {
if (profileId.equalsIgnoreCase("test1")) {
callback.completion(new ProfileAgeRange(0, 4));
} else if (profileId.equalsIgnoreCase("test2")) {
callback.completion(new ProfileAgeRange(9, 10));
} else {
callback.completion(new ProfileAgeRange(null));
}
}
}; Parameters
profileId- identifies the current user/profile sessioncallback- Return a valid ProfileAgeRange, or nil if available
Notes
- Supports async operations (network calls, database lookups, user prompts, etc.)
- If not implemented or nil is returned:
- No age range is set
- The server may still enforce restrictions based on previously stored data (see Cross-Device Behavior)
3. Setting the Interface
Register your age range provider before calling startSDK:
// register via OTCustomConfigurator
OTCustomConfigurator.setupProfileAgeRangeManager(ageRangeProvider)
OTPublishersHeadlessSDK(context).startSDK(...)OTCustomConfigurator.setupProfileAgeRangeManager(ageRangeProvider);
new OTPublishersHeadlessSDK(context).startSDK(...);Public APIs
checkandLogConsent
checkandLogConsentTriggers an age range validation and logs consent if the value has changed.
Use this method when the user’s age range may have changed after SDK initialization, such as:
- User profile updates
- Account switching
- User login / re-authentication
OTPublishersHeadlessSDK.AppPermissionType.AGE_GATE.checkAndLogConsent(object : OTCallback {
override fun onSuccess(response: OTResponse) {
// Age range has been checked and logged if changed.
}
override fun onFailure(response: OTResponse) {
// Handle failure
}
})OTPublishersHeadlessSDK.AppPermissionType.AGE_GATE.checkAndLogConsent(
new OTCallback() {
@Override
public void onSuccess(@NonNull OTResponse response) {
// Age range has been checked and logged if changed
}
@Override
public void onFailure(@NonNull OTResponse response) {
// Handle failure
}
});When invoked, the SDK performs the following steps:
- Calls the
getProfileAgeRangeinterface to retrieve the latest age range - Compares the value against the previously stored range
- If the value has changed:
- Updates local storage
- Logs the new value to the CMP API using the
AGEGATE_RANGEinteraction type
- If unchanged:
- No action is taken
When to Call:
Call this API in any scenario where the user’s age range may be updated:
- After profile updates that include age or date of birth changes
- After account switching or user re-authentication
Note: You do not need to call this method during startSDK. The SDK automatically performs this check during initialization.
getConsentStatus
getConsentStatus Returns the consent status for a purpose group.
val status = OTPublishersHeadlessSDK(context).getConsentStatus("C0005")int status = new OTPublishersHeadlessSDK(context).getConsentStatus("C0005");Returns:
1= consent given0= consent denied (including age-restrictions)-1= invalid/unknown purpose group ID
Age Gate Impact:
- returns
0automatically for any purpose linked to a restricted age range. No additional code is required
getPurposeConsentLocal
getPurposeConsentLocal Returns the most up-to-date consent value available on the device, including values that may not yet be synced with the server.
val status = OTPublishersHeadlessSDK(context).getPurposeConsentLocal("C0005")int status = new OTPublishersHeadlessSDK(context).getPurposeConsentLocal("C0005");Age Gate Impact:
- Returns
0(denied) for any purpose associated with a restricted age group - Behavior is consistent with
getConsentStatusfor age-restricted purposes
updatePurposeConsent
updatePurposeConsent Updates the consent value for a purpose group programmatically.
OTPublishersHeadlessSDK(context).updatePurposeConsent("C0005", true)new OTPublishersHeadlessSDK(context).updatePurposeConsent("C0005", true);With Age-Based Consent supported SDKs:
- The SDK prevents enabling consent for age-restricted purposes
- Any attempt to set consent to true is silently ignored
Without Age-Based Consent supported SDKs (older SDKs):
- The update may succeed locally
- The CMP API will override the value to
0(denied) during the next sync
Behavior Changes
Preference Center
Restricted Users
If a user’s age range overlaps with a configured restricted age group:
- The consent toggle for associated purposes is hidden (
consentToggleStatus: -1) - Consent is automatically set to
0(denied) - The user cannot provide consent for those purposes
Non-Restricted Users
If the user’s age range does not overlap with any restricted age group:
- All consent toggles remain visible and interactive
- Standard consent behavior applies
Age Range Overlap Logic
A user is considered restricted if their age range shares any intersection with a restricted age group. Partial overlap also results in restriction.
Logic:
restricted = (user.lowerBound <= group.upperBound) && (user.upperBound >= group.lowerBoundExamples:
Restricted age group: 0–15:
| User Age Range | Result | Reason |
|---|---|---|
| [0,18] | Restricted | Possible overlap (e.g., age 14) |
| [14,16] | Restricted | Overlaps with 14-15 |
| [10,12] | Restricted | Fully within restricted range |
| [18,25] | Not Restricted | No overlap |
| [18, null] | Not Restricted | Open-ended (18+) does not intersect |
Cross-Device Behavior (CRO Users)
For environments with Cross-Device Consent or Authenticated Consent enabled, age-based restrictions are enforced server-side and persist across devices.
How it Works
Once a user’s age range is logged for a profile from any device:
- The CMP API server stores the age range
- All subsequent consent responses for that profile enforce the same restrictions
- Enforcement applies across:
- Devices
- Sessions
- SDK versions
SDKs with Age Range Supported (Recommended)
This configuration provides complete enforcement and is strongly recommended.
- The server returns
consentToggleStatus: -1for restricted purposes - The SDK:
- Hides restricted toggles in the UI
- Prevents consent from being enabled via public APIs (e.g.,
updatePurposeConsent)
- Enforcement is applied at both:
- UI level (toggle hidden)
- API level (updates blocked)
SDKs without Age Range Supported (Older SDK Versions)
Note: Applies to CRO-enabled environments where server-side enforcement is active.
For SDKs that support CMP API but do not include Age-Based Consent logic:
- The server continues to send
consentToggleStatus: -1for restricted purposes - The SDK:
- Correctly hides toggles in the Preference Center (UI remains consistent)
- Does not enforce age restrictions at the API level
Behavior Differences
updatePurposeConsentmay still allow local updates for restricted purposes- However:
- These updates are not authoritative
- The CMP API server will override consent to 0 (denied) during the next sync
- The toggle remains hidden in the UI regardless of local changes
Result
- Users cannot bypass restrictions through the UI
- Temporary local inconsistencies may occur if consent is updated programmatically
- The server ensures the final persisted state is always compliant
Legacy SDKs without CMP API
Note: This section applies only to CRO (cross-device) scenarios where server-side enforcement is required.
This behavior applies to legacy SDKs that:
- Use the legacy mobile-data API flow (pre-CMP API)
- Include legacy Web SDKs
- Include CTV SDKs not yet migrated to CMP API
Behavior Differences
- These SDKs do not support
consentToggleStatus: -1 - As a result:
- Consent toggles may remain visible in the UI
- Users may be able to enable consent locally
However:
- When consent is synchronized with the server:
- The CMP API will override the value to 0 (denied) for restricted purposes
- Any locally enabled state is not persisted
Result
- Users may temporarily see consent toggled ON on the device
- After the next server sync:
- The toggle reflects the correct OFF (denied) state
Anonymous to Known User Consent Transfer
When a user transitions from an anonymous session to an authenticated (known) user, the SDK transfers consent using the SYNC_PROFILE interaction.
With Age-Based Consent enabled, this process includes additional safeguards to ensure age-based restrictions are correctly enforced.
Default Behavior (Without Age-Based Consent Enabled)
- The user provides consent during an anonymous session
- The user authenticates (logs in)
- The SDK invokes
handleAuthenticatedConsent,sending aSYNC_PROFILErequest to the CMP API - The CMP API compares:
- Anonymous consent
- Stored preferences for the known profile
- The most recent consent value is applied for each purpose
Behavior with Age-Based Consent Enabled
When processing a SYNC_PROFILE request, the CMP API checks whether the known profile already has an associated age range.
If the known profile has an Age Range:
- Consent for Age-Based Consent–linked purposes is not transferred, regardless of recency
- The server preserves the existing age-restricted consent state
- Consent for non-linked purposes continues to follow standard behavior (latest value wins)
If the known profile does not have an age range:
- All consent transfers normally (latest value wins)
- Age-based restrictions are not applied until an age range is provided
Important: The anonymous user’s age range is not carried over to the known profile. This applies even if the anonymous session included age range data. This is because:
- Age range is identity-specific and may not apply across users (e.g., shared devices)
- The authenticated profile must provide its own age range
- Enforcement relies on the age range returned via the
getProfileAgeRangedelegate
Implementation Guidance
To ensure proper enforcement:
- Implement
getProfileAgeRangefor authenticated users (recommended) - Optionally implement it for anonymous users if age data is collected pre-login
- When a user authenticates:
- The SDK calls
getProfileAgeRangewith the new profile ID - Your app should return the correct age range for that user
- The SDK calls
If the age range differs from the previous session, the SDK:
- Detects the change
- Logs it using the
AGEGATE_RANGEinteraction
Example Flow
- Anonymous user (age 14–17) consents → Age Gate restricts linked purposes
- User logs in as [email protected]
- SDK calls
handleAuthenticatedConsent→SYNC_PROFILErequest sent - CMP API evaluates the known profile:
- Age 14–17 → restrictions remain; consent for linked purposes is not transferred
- Age 18–25 → no restriction; consent transfers normally
- No age range → all consent transfers; restrictions applied only after age is provided
- SDK calls
getProfileAgeRange("[email protected]") → app provides age range → SDK stores and logs
Recommendation
To ensure consistent and reliable behavior across devices:
- Upgrade all applications to the latest CMP API-enabled SDK with Age-Based Consent supported
This enables:
- Full UI enforcement (hidden toggles)
- Full API enforcement (restricted updates blocked)
- Elimination of temporary consent inconsistencies
- Correct handling of anonymous-to-known user consent transfer
End-To-End Flow
-
Admin configures restricted age groups on the OneTrust Console and links purpose groups/categories to them.
-
App sets the
OTCustomConfiguratordelegate with thegetProfileAgeRangeimplementation. -
App calls
startSDK. -
SDK internally calls
getProfileAgeRangeto request the user's age range from the app. -
If the app provides a range:
- SDK stores it locally.
- SDK logs the age range to the CMP API server with an
AGEGATE_RANGEconsent interaction. - Server stores the age range for the profile and enforces restrictions in subsequent responses.
- SDK hides toggles for restricted purposes in the Preference Center.
-
If the app returns nil (age unknown):
- SDK does not send an age range to the server.
- The server checks if an age range already exists for this profile from a prior interaction or another device.
- If found, the server still enforces age-based restrictions.
- If no age range exists anywhere, no restrictions are applied.
-
When the user's age changes (profile update, account switch):
- App calls
AppPermissionType.ageGate.checkAndLogConsent - SDK re-invokes the delegate, compares with stored range.
- If changed, updates storage and logs to server.
- Preference Center reflects the updated restriction state.
- App calls
Important Notes
-
App must provide age range
The SDK does not determine user age. Your app must supply it using internal data (e.g., profile, registration, Date of birth). -
No system-level APIs
The SDK does not use platform APIs (e.g.,Android AccountManager). It requires the app user's age, not the device owner's. -
Existing Age Gate UI unchanged
showConsentUIandgetAgeGatePromptValue()are unaffected and operate independently. -
Supported platforms
Android and Android TV (more platforms coming). -
Consistent API naming
APIs are aligned across platforms (e.g.,ProfileAgeRange, getProfileAgeRange, checkAndLogConsent, getConsentStatus, updatePurposeConsent).
Updated about 5 hours ago