Skip to main content

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.

The Unity SDK is currently in alpha. APIs and behavior may change between releases.
Who is this for? Unity engineers building PC desktop games on Windows or macOS who want typed in-game tracking, identity, and offline-safe queuing. Linux is not supported yet.
The Immutable Unity SDK tracks player behavior inside Unity games. Session lifecycle and a launch event are captured automatically. In-game moments like progressions, purchases, and milestones are triggered by your code. Events flow to the same pipeline as the Tracking Pixel and Web SDK.

What You Need

Installation

Install the package via Unity Package Manager using a Git URL.
  1. In Unity, open Window → Package Manager.
  2. Click the + button in the top-left, then Add package from git URL….
  3. Paste the URL below and click Add:
https://github.com/immutable/unity-immutable-sdk.git?path=src/Packages/Audience#main
The package appears in your project as Immutable Audience under Packages in the Project window.
The #main ref tracks the latest commit on the SDK main branch. For reproducible builds, replace #main with a specific commit SHA, for example: ?path=src/Packages/Audience#a1b2c3d.
To update later, re-open Package Manager, click the package, and press Update, or edit the entry in Packages/manifest.json directly.

Quick Start

Prefer learning from a running project? A working Unity sample lives at unity-immutable-sdk/examples/audience. Clone the repository, set your publishable key, and run.
1

Initialize the SDK at boot

Call ImmutableAudience.Init once at game launch. You can mark a static method with [RuntimeInitializeOnLoadMethod] to have Unity call it before any scene loads.
using Immutable.Audience;
using UnityEngine;

public static class Analytics
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Init()
    {
        ImmutableAudience.Init(new AudienceConfig
        {
            PublishableKey = "YOUR_PUBLISHABLE_KEY",
            Consent = ConsentLevel.Anonymous,
            DistributionPlatform = DistributionPlatforms.Steam,
            Debug = true,
        });
    }
}
Consent defaults to ConsentLevel.None, which suppresses all tracking. The example above sets Anonymous directly so events flow immediately. For an in-game privacy prompt, see the next step.
2

Set consent

The SDK uses a three-tier consent model (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.
// Player accepts anonymous tracking
ImmutableAudience.SetConsent(ConsentLevel.Anonymous);

// Player logs in and accepts full tracking
ImmutableAudience.SetConsent(ConsentLevel.Full);

// Player revokes consent (clears identity, purges queued events, stops collection)
ImmutableAudience.SetConsent(ConsentLevel.None);
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.
3

Track events

Use ImmutableAudience.Track to log a player action. The SDK ships with predefined events for common player actions and accepts any custom event string.
// Predefined event with typed properties
ImmutableAudience.Track(new Purchase
{
    Currency = "USD",
    Value = 9.99m,
    ItemName = "Sword of Truth",
});

// Custom event with any properties
ImmutableAudience.Track("tutorial_complete", new Dictionary<string, object>
{
    ["stepCount"] = 5,
});
4

Identify users

Links anonymous activity to a known player. Call on login or account connection. Requires Full consent.
ImmutableAudience.Identify(
    userId: "76561190000000000",
    identityType: IdentityType.Steam,
    traits: new Dictionary<string, object>
    {
        ["personaName"] = "Player1",
    });
5

Clean up on exit

Called automatically on Application.quitting. For manual teardown:
// Manual teardown: flush pending events, then stop the SDK.
await ImmutableAudience.FlushAsync();
ImmutableAudience.Shutdown();

Complete Example

using System.Collections.Generic;
using Immutable.Audience;
using UnityEngine;

public static class Analytics
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static async void Init()
    {
        // 1. Initialize
        ImmutableAudience.Init(new AudienceConfig
        {
            PublishableKey = "YOUR_PUBLISHABLE_KEY",
        });

        // 2. Set consent after privacy prompt
        ImmutableAudience.SetConsent(ConsentLevel.Anonymous);

        // 3. Track player actions
        ImmutableAudience.Track(new Purchase { Currency = "USD", Value = 9.99m });

        // 4. Identify after login (requires Full consent)
        ImmutableAudience.SetConsent(ConsentLevel.Full);
        ImmutableAudience.Identify("76561190000000000", IdentityType.Steam, new Dictionary<string, object>
        {
            ["personaName"] = "Player1",
        });

        // 5. Clean up on exit (auto-called on Application.quitting)
        await ImmutableAudience.FlushAsync();
        ImmutableAudience.Shutdown();
    }
}

Verify the Integration

After Init, the SDK exposes its state through read-only properties on ImmutableAudience. Log them to confirm a healthy install:
Debug.Log($"Initialized: {ImmutableAudience.Initialized}");
Debug.Log($"AnonymousId: {ImmutableAudience.AnonymousId}");
Debug.Log($"SessionId: {ImmutableAudience.SessionId}");
Debug.Log($"QueueSize: {ImmutableAudience.QueueSize}");
Pair with an OnError callback in your AudienceConfig to surface failures (see Error Handling):
OnError = err => Debug.LogWarning($"{err.Code}: {err.Message}"),
A healthy install:
PropertyHealthy value
Initializedtrue
AnonymousIdnon-null GUID
SessionIdnon-null GUID once session_start has fired
QueueSize1 immediately after a Track call, then 0 once flushed
Unity Consoleno SDK warnings, no OnError invocations

API Reference

All methods are static on Immutable.Audience.ImmutableAudience and are safe to call from any thread after Init returns.
Starts the SDK. Call once at game launch (see the Quick Start). Subsequent calls are ignored with a warning.Throws ArgumentNullException if config is null, and ArgumentException if PublishableKey is empty.
ImmutableAudience.Init(new AudienceConfig
{
    PublishableKey = "YOUR_PUBLISHABLE_KEY",
    Consent = ConsentLevel.None,
    DistributionPlatform = DistributionPlatforms.Steam,
    Debug = true,
    OnError = err => Debug.LogWarning($"{err.Code}: {err.Message}"),
});
AudienceConfig fields:
FieldTypeRequiredDefaultDescription
PublishableKeystringYes(none)API key from Hub. Keys starting with pk_imapik-test- route to the sandbox environment. All other keys route to production.
BaseUrlstring?No(auto from key)Override the API base URL. Leave null to use the key-based default routing.
ConsentConsentLevelNoNoneInitial consent level. The SDK collects nothing until this is Anonymous or higher.
DistributionPlatformstring?NonullThe 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).
DebugboolNofalseEnable SDK warnings via UnityEngine.Debug.Log. Disable in release builds.
FlushIntervalSecondsintNo5How often pending events are flushed to the backend.
FlushSizeintNo20Flush as soon as this many events are queued.
OnErrorAction<AudienceError>?NonullCallback 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).
PersistentDataPathstring?No(auto from Unity)Directory for the SDK identity, consent, and queued-event files. The Unity integration fills this in from Application.persistentDataPath.
PackageVersionstringNoSDK package versionLibrary version sent on every message. Override only if you need to report a different version (e.g. wrapping the SDK in your own package).
ShutdownFlushTimeoutMsintNo2000Maximum time Shutdown waits for the final flush.

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:
ConstantValue
DistributionPlatforms.Steam"steam"
DistributionPlatforms.Epic"epic"
DistributionPlatforms.GOG"gog"
DistributionPlatforms.Itch"itch"
DistributionPlatforms.Standalone"standalone"
If you ship on a platform not in this list, pass the lowercase short name as a string.
Changes the consent level. Pass 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.
ImmutableAudience.SetConsent(ConsentLevel.Full);
The new level is persisted to disk so it survives the next launch. The SDK also notifies the backend asynchronously for audit logging. Failures fire OnError with AudienceErrorCode.ConsentSyncFailed but do not affect local state.
Sends a predefined event. See the Data Dictionary for available event classes and their schemas.
ParameterTypeDescription
evtIEventThe typed event instance. Null events are dropped with a log warning.
Requires: Anonymous or Full consent. Calls at None are silently dropped.
ImmutableAudience.Track(new Purchase { Currency = "USD", Value = 9.99m });
Sends a custom event with arbitrary properties. No validation runs on this overload. Prefer Track(IEvent) for predefined events. Property values can be any JSON-serializable primitive or string. Strings are truncated at 256 characters.
ParameterTypeDescription
eventNamestringRequired, non-empty. The custom event name. Null or empty values are dropped with a log warning.
propertiesDictionary<string, object>?Optional. Event properties. Defaults to null.
Requires: Anonymous or Full consent.
ImmutableAudience.Track("tutorial_complete", new() { ["stepCount"] = 5 });
Attaches a known user ID to subsequent events. Use IdentityType.Custom for providers not in the enum.
ParameterTypeDescription
userIdstringRequired, 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.
identityTypeIdentityTypeRequired. Which provider the ID comes from (see table below).
traitsDictionary<string, object>?Optional. Custom user traits (e.g. personaName). Defaults to null.
Requires: Full consent. Calls at lower levels are dropped with a log warning.
ImmutableAudience.Identify("76561190000000000", IdentityType.Steam, new() { ["personaName"] = "Player1" });
Identity types:
ValueProvider
IdentityType.PassportImmutable Passport
IdentityType.SteamSteam
IdentityType.EpicEpic Games
IdentityType.GoogleGoogle
IdentityType.AppleApple
IdentityType.DiscordDiscord
IdentityType.EmailEmail address
IdentityType.CustomCustom identity system
Links two user IDs that belong to the same player. Call when a player connects a second account (e.g. a player with an internal game account ID later signs in via Steam).
ParameterTypeDescription
fromIdstringRequired, non-empty. The account being linked.
fromTypeIdentityTypeRequired. The provider for fromId.
toIdstringRequired, non-empty. The player’s main account.
toTypeIdentityTypeRequired. The provider for toId.
Requires: Full consent. Both fromType and toType are required for data-deletion matching.
ImmutableAudience.Alias("76561190000000000", IdentityType.Steam, "internal-player-id-12345", IdentityType.Custom);
Wipes the current player’s identity, generates a fresh anonymous ID, and discards queued events (memory and disk) so the next player on the device isn’t attributed to the previous one. Call when a player logs out.To send queued events before they’re discarded:
await ImmutableAudience.FlushAsync();
ImmutableAudience.Reset();
Asks the backend to erase the player’s data. Returns a Task you can await for acknowledgement, or discard for fire-and-forget.
ParameterTypeDescription
userIdstring?Optional. The known user ID to target. When omitted (or null), the current anonymous ID is used.
await ImmutableAudience.DeleteData("76561190000000000");
Erasure is server-side. After the request is accepted, also call Reset on the client so the next session isn’t linked to the deleted user. See the REST API for the request shape.
Sends all queued events to the server immediately. Returns a 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.
await ImmutableAudience.FlushAsync();
Flushes any pending events (capped at 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.
ImmutableAudience.Shutdown();
Read-only properties for inspecting SDK state. Use these to verify the SDK is initialized, gate UI elements that depend on consent state (the Unity SDK does not currently ship dedicated CanTrack or CanIdentify helpers, so read CurrentConsent and compare against ConsentLevel.Anonymous or ConsentLevel.Full instead), or watch the queue size during development.
PropertyTypeDescription
InitializedboolTrue between Init() and Shutdown()
CurrentConsentConsentLevelThe current consent level
UserIdstring?Set by the most recent Identify call. Null before Identify, after Reset, or when consent is below Full.
AnonymousIdstring?Persistent ID separate from UserId and SessionId. Null while consent is None.
SessionIdstring?The current session’s GUID. Null while consent is None.
QueueSizeintNumber of unsent events (memory + disk)
// Gate a tracking-dependent UI element on consent
if (ImmutableAudience.CurrentConsent >= ConsentLevel.Anonymous)
{
    ShowAnalyticsBadge();
}

// Force-flush the queue when it grows large
if (ImmutableAudience.Initialized && ImmutableAudience.QueueSize > 100)
{
    await ImmutableAudience.FlushAsync();
}

Event Delivery

  • Events are batched and flushed every 5 seconds or when 20 events accumulate. Configure via the FlushIntervalSeconds and FlushSize fields on AudienceConfig (see Init).
  • Application.quitting triggers a final flush capped at ShutdownFlushTimeoutMs (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 an OnError callback when configuring Init to observe them. AudienceError.Code is one of:
CodeDescription
FlushFailedAn 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.
ValidationRejectedThe server rejected an event batch with a 4xx status. The batch was dropped. Retrying will not help (typically a malformed payload).
ConsentSyncFailedFailed 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.
NetworkErrorA 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.
ConsentPersistFailedFailed to persist the consent level to disk. The in-memory level still applies but reverts on next launch.

FAQ

Not yet. The Unity SDK currently runs on Windows and macOS only. Linux desktop support is planned for a future release.
Both Mono and IL2CPP are fully supported. Choose either in Edit → Project Settings → Player → Other Settings → Scripting Backend.
Yes. Init, Track, session lifecycle, and flush all work in Play Mode and Edit Mode test runners. Events fired in the Editor reach the same pipeline as builds. Use a sandbox key (pk_imapik-test-...) during development to keep dev events out of production reporting.
Yes. The three integrations cover different parts of your player journey. Unity captures in-game actions, the Web SDK covers web games and single-page apps, and the Tracking Pixel handles marketing pages and landing sites. All events flow to the same pipeline. See the Data Dictionary for the full per-event schema.
Erasure is server-side. From your backend, call 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.
await ImmutableAudience.DeleteData("76561190000000000");
ImmutableAudience.Reset();
Under 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.

Next Steps

Sample app

Working Unity project with Init, consent, Track, and Identify wired up

Data Dictionary

Full reference of collected data at each consent level

Web SDK

Companion SDK for web games and landing pages

REST API

Send events directly from your backend or game server

API Keys

Manage your publishable and secret keys