Documentation Index
Fetch the complete documentation index at: https://docs.immutable.com/llms.txt
Use this file to discover all available pages before exploring further.
What You Need
- An Immutable Hub account with a project (get started here)
- A publishable key from your project settings (API keys guide)
- A target build platform. Tested on:
- Windows 10+
- macOS Sequoia 15.7.4+
- Linux Ubuntu 22.04 LTS+
- iOS 13+
- Android 5.0+ (API level 21+)
- A Unity project on a supported version:
- A Unity project using Mono or IL2CPP as the scripting backend (both are fully supported)
Installation
Install the package via Unity Package Manager using a Git URL.- In Unity, open Window → Package Manager.
- Click the + button in the top-left, then Add package from git URL….
- Paste the URL below and click Add:
Packages/manifest.json directly.
Quick Start
Initialize the SDK at boot
ImmutableAudience.Init once at game launch. You can mark a static method with [RuntimeInitializeOnLoadMethod] to have Unity call it before any scene loads.Set consent
None, Anonymous, Full). The default is None. The SDK does not collect anything until you explicitly raise it. The SDK does not provide a consent UI. Build your own in-game prompt and call SetConsent when the player responds.SetConsent takes effect immediately. Lowering the level purges queued events the new level no longer permits. See the Data Dictionary for what each level collects.Track events
ImmutableAudience.Track to log a player action. The SDK ships with predefined events for common player actions and accepts any custom event string.Identify users
Full consent.Complete Example
Verify the Integration
After Init, the SDK exposes its state through read-only properties onImmutableAudience. Log them to confirm a healthy install:
OnError callback in your AudienceConfig to surface failures (see Error Handling):
| Property | Healthy value |
|---|---|
Initialized | true |
AnonymousId | non-null GUID |
SessionId | non-null GUID once session_start has fired |
QueueSize | 1 immediately after a Track call, then 0 once flushed |
| Unity Console | no SDK warnings, no OnError invocations |
Mobile
Mobile adds opt-in device-level attribution signals for iOS and Android.Attribution opt-in
Two gates must both be set before any mobile attribution data ships:| Gate | Where |
|---|---|
Build-time AUDIENCE_MOBILE_ATTRIBUTION | Player Settings → Other Settings → Scripting Define Symbols. Set separately for each player target (iOS and Android). |
Runtime EnableMobileAttribution = true | AudienceConfig passed to Init. Controls whether attribution data is collected at runtime. |
AUDIENCE_MOBILE_ATTRIBUTION does three things: adds the AD_ID manifest permission on Android, compiles in native attribution code, and switches the iOS privacy manifest to the tracking variant (NSPrivacyTracking = true).
If neither gate is set, the build ships without attribution code: no AD_ID manifest permission, no native tracking, and NSPrivacyTracking = false in the iOS privacy manifest.
Platform setup
- iOS
- Android
NSUserTrackingUsageDescription string. Apple rejects builds that omit this key. Create the asset once per Unity project:- Assets → Create → Immutable Audience → Mobile Build Settings
- Set Tracking Usage Description to a specific description of what you collect and why. Apple rejects generic copy.
RequestTrackingAuthorizationAsync at a contextually appropriate moment. The IDFA, when authorized, ships automatically in the next game_launch. A tracking_authorization_changed event fires on any subsequent status transition.ATT requires iOS 14+. On iOS 13 the call resolves to NotDetermined and IDFA is unavailable. All other SDK features work normally.PrivacyInfo.xcprivacy that Unity merges into the generated Xcode project. The post-processor selects the correct variant based on the AUDIENCE_MOBILE_ATTRIBUTION define:| Build | NSPrivacyTracking | Declared data types |
|---|---|---|
| Default | false | IDFV (NSPrivacyCollectedDataTypeDeviceID) |
AUDIENCE_MOBILE_ATTRIBUTION | true | IDFV + IDFA (NSPrivacyCollectedDataTypeAdvertisingData) |
NSPrivacyAccessedAPITypes in the manifest reflects Unity engine internals only.API Reference
All methods are static onImmutable.Audience.ImmutableAudience and are safe to call from any thread after Init returns.
Init(AudienceConfig)
Init(AudienceConfig)
ArgumentNullException if config is null, and ArgumentException if PublishableKey is empty.AudienceConfig fields:| Field | Type | Required | Default | Description |
|---|---|---|---|---|
PublishableKey | string | Yes | (none) | API key from Hub. |
Consent | ConsentLevel | No | None | Initial consent level. The SDK collects nothing until this is Anonymous or higher. |
DistributionPlatform | string? | No | null | The platform the build is shipping on. Init normalizes this to lowercase, so case at the call site does not matter. Use DistributionPlatforms.* constants for the standard values (see below). |
Debug | bool | No | false | Enable SDK warnings via UnityEngine.Debug.Log. Disable in release builds. |
TestMode | bool | No | false | Mark all events sent in this session as test traffic. Use during development or QA to separate test events from production data in analytics. |
FlushIntervalSeconds | int | No | 5 | How often pending events are flushed to the backend. |
FlushSize | int | No | 20 | Flush as soon as this many events are queued. |
OnError | Action<AudienceError>? | No | null | Callback fired on errors. Runs on a background thread. Marshal to the main thread before touching Unity APIs. AudienceError.Code is one of FlushFailed, ValidationRejected, ConsentSyncFailed, NetworkError, ConsentPersistFailed (see Error Handling for descriptions). |
PersistentDataPath | string? | No | (auto from Unity) | Directory for the SDK identity, consent, and queued-event files. The Unity integration fills this in from Application.persistentDataPath. |
PackageVersion | string | No | SDK package version | Library version sent on every message. Override only if you need to report a different version (e.g. wrapping the SDK in your own package). |
ShutdownFlushTimeoutMs | int | No | 2000 | Maximum time Shutdown waits for the final flush. |
EnableMobileAttribution | bool | No | false | Opts into mobile attribution signals (ATT/IDFA/SKAdNetwork on iOS, GAID/Install Referrer on Android). Both this flag and the AUDIENCE_MOBILE_ATTRIBUTION scripting define must be set. See Mobile. |
Distribution platforms
DistributionPlatform is a free-form string sent in the distributionPlatform property of the game_launch event. Init lowercases the value before it goes on the wire. Use the DistributionPlatforms constants when one matches:| Constant | Value |
|---|---|
DistributionPlatforms.Steam | "steam" |
DistributionPlatforms.Epic | "epic" |
DistributionPlatforms.GOG | "gog" |
DistributionPlatforms.Itch | "itch" |
DistributionPlatforms.Standalone | "standalone" |
SetConsent(ConsentLevel level)
SetConsent(ConsentLevel level)
ConsentLevel.None, ConsentLevel.Anonymous, or ConsentLevel.Full. Takes effect immediately. Lowering the level purges queued events that the new level no longer permits. Lowering to None also clears the anonymous ID.OnError with AudienceErrorCode.ConsentSyncFailed but do not affect local state.Track(IEvent)
Track(IEvent)
| Parameter | Type | Description |
|---|---|---|
evt | IEvent | The typed event instance. Null events are dropped with a log warning. |
Anonymous or Full consent. Calls at None are silently dropped.Track(string eventName, Dictionary<string, object>? properties)
Track(string eventName, Dictionary<string, object>? properties)
Track(IEvent) for predefined events. Property values can be any JSON-serializable primitive or string. Strings are truncated at 256 characters.| Parameter | Type | Description |
|---|---|---|
eventName | string | Required, non-empty. The custom event name. Null or empty values are dropped with a log warning. |
properties | Dictionary<string, object>? | Optional. Event properties. Defaults to null. |
Anonymous or Full consent.Identify(string userId, IdentityType identityType, Dictionary? traits = null)
Identify(string userId, IdentityType identityType, Dictionary? traits = null)
IdentityType.Custom for providers not in the enum.| Parameter | Type | Description |
|---|---|---|
userId | string | Required, non-empty. The player’s identifier in the chosen provider. The SDK silently drops calls with null or empty userId (a Debug.LogWarning is emitted) so a missing ID does not crash the game. Gate calls on a populated value during development. |
identityType | IdentityType | Required. Which provider the ID comes from (see table below). |
traits | Dictionary<string, object>? | Optional. Custom user traits (e.g. personaName). Defaults to null. |
Full consent. Calls at lower levels are dropped with a log warning.| Value | Provider |
|---|---|
IdentityType.Passport | Immutable Passport |
IdentityType.Steam | Steam |
IdentityType.Epic | Epic Games |
IdentityType.Google | |
IdentityType.Apple | Apple |
IdentityType.Discord | Discord |
IdentityType.Email | Email address |
IdentityType.Custom | Custom identity system |
Alias(string fromId, IdentityType fromType, string toId, IdentityType toType)
Alias(string fromId, IdentityType fromType, string toId, IdentityType toType)
| Parameter | Type | Description |
|---|---|---|
fromId | string | Required, non-empty. The account being linked. |
fromType | IdentityType | Required. The provider for fromId. |
toId | string | Required, non-empty. The player’s main account. |
toType | IdentityType | Required. The provider for toId. |
Full consent. Both fromType and toType are required for data-deletion matching.Reset()
Reset()
DeleteData(string? userId = null)
DeleteData(string? userId = null)
Task you can await for acknowledgement, or discard for fire-and-forget.| Parameter | Type | Description |
|---|---|---|
userId | string? | Optional. The known user ID to target. When omitted (or null), the current anonymous ID is used. |
Reset on the client so the next session isn’t linked to the deleted user. See the REST API for the request shape.FlushAsync()
FlushAsync()
Task that completes when the queue is empty or a backoff window prevents further sends. Call before Reset (which discards queued events) or Shutdown (which is capped at ShutdownFlushTimeoutMs) if you want every pending event delivered first.Shutdown()
Shutdown()
ShutdownFlushTimeoutMs, default 2 seconds) and stops the SDK. Called automatically on Application.quitting. You do not need to call it yourself unless you want to control the flush timing or reinitialize with new config.RequestTrackingAuthorizationAsync() (iOS only)
RequestTrackingAuthorizationAsync() (iOS only)
Diagnostic getters
Diagnostic getters
CanTrack or CanIdentify helpers, so read CurrentConsent and compare against ConsentLevel.Anonymous or ConsentLevel.Full instead), or watch the queue size during development.| Property | Type | Description |
|---|---|---|
Initialized | bool | True between Init() and Shutdown() |
CurrentConsent | ConsentLevel | The current consent level |
UserId | string? | Set by the most recent Identify call. Null before Identify, after Reset, or when consent is below Full. |
AnonymousId | string? | Persistent ID separate from UserId and SessionId. Null while consent is None. |
SessionId | string? | The current session’s GUID. Null while consent is None. |
QueueSize | int | Number of unsent events (memory + disk) |
Event Delivery
- Events are batched and flushed every 5 seconds or when 20 events accumulate. Configure via the
FlushIntervalSecondsandFlushSizefields onAudienceConfig(seeInit). Application.quittingtriggers a final flush capped atShutdownFlushTimeoutMs(default 2 seconds).- Events not flushed before quit are persisted to disk and shipped on the next launch. Events older than 30 days are discarded.
Error Handling
Flushes run on a background thread with automatic retries. Most failures don’t surface to your code. Pass anOnError callback when configuring Init to observe them.
AudienceError.Code is one of:
| Code | Description |
|---|---|
FlushFailed | An event batch failed to flush. Either a local storage read error (batch dropped) or a non-2xx, non-4xx server response (typically 5xx). On 5xx, the batch is retained and retried with exponential backoff. |
ValidationRejected | The server rejected an event batch with a 4xx status. The batch was dropped. Retrying will not help (typically a malformed payload). |
ConsentSyncFailed | Failed to sync a consent change to the backend. The local consent level has already been applied. The server-side audit log may be out of date. |
NetworkError | A network call failed (exception, timeout, or non-2xx response on data deletion). Event batches are retained for retry. Data-delete requests are not retried automatically. |
ConsentPersistFailed | Failed to persist the consent level to disk. The in-memory level still applies but reverts on next launch. |
FAQ
Which scripting backends are supported?
Which scripting backends are supported?
Does the SDK work in the Editor?
Does the SDK work in the Editor?
TestMode = true in your AudienceConfig during development to mark events as test traffic and keep them separate from production data in analytics.What happens if I call Track before consent is set?
What happens if I call Track before consent is set?
SetConsent(ConsentLevel.Anonymous) (or higher) runs, subsequent Track calls flow normally. To verify state during development, read ImmutableAudience.CurrentConsent or the other diagnostic getters.Can I use the Unity SDK alongside the Web SDK or Tracking Pixel?
Can I use the Unity SDK alongside the Web SDK or Tracking Pixel?
How do I handle a GDPR erasure request?
How do I handle a GDPR erasure request?
DELETE /v1/audience/data with the user’s userId or anonymousId. See Deleting User Data for the request shape. From inside the game, call ImmutableAudience.DeleteData(userId) and then ImmutableAudience.Reset() so subsequent activity isn’t linked to the erased user.Where does the SDK store its data?
Where does the SDK store its data?
Application.persistentDataPath in a folder named imtbl_audience. The folder contains the anonymous ID, the persisted consent level, and any queued events that haven’t been flushed yet. Deleting it resets the SDK’s local state on next launch.How does identity work across the marketing site and the game?
How does identity work across the marketing site and the game?
Identify with the same userId on both surfaces at login. Events from both sessions are attributed to that player automatically, no Alias call needed.Alias is only needed when the same player uses different provider IDs on different surfaces. For example, if the player is identified as a Steam user in-game but later links a Passport account, call Alias to tell the backend they are the same person. See Identity Stitching in the Data Dictionary.