ReflectDocs
Browse docsOverview
Unity SDK

Identity & users

install_uuid, SetUserId, anonymous → known stitching, and the device identifiers Reflect collects.

Keys you ship in the SDK

Three credentials go into ReflectConfig. Two are public, one is secret.

CredentialPer-app?Per-platform?Secret?
CompanyKey (co_live_…)No — one per companyNo — same iOS & AndroidNo — public
AppKey (app_live_…)YesYes — different iOS vs AndroidNo — public
SigningSecretYes — paired with AppKeyYes — different iOS vs AndroidYes — never commit
iOS and Android need separate AppKey + SigningSecret pairs.

A Reflect app row maps to one platform — different bundle IDs, different store URLs, different SKAN config, different analytics rollups. Sharing keys returns 401 app_company_mismatch. Use #if UNITY_IOS in Unity to pick the right pair per build.

new ReflectConfig {
    BaseUrl       = "https://api.reflect.cloud",
    CompanyKey    = "co_live_…",                  // shared
    #if UNITY_IOS
    AppKey        = "app_live_<ios>",             // different per platform
    SigningSecret = "<ios-secret>",
    #else
    AppKey        = "app_live_<android>",
    SigningSecret = "<android-secret>",
    #endif
};

Find them in the admin: Settings for CompanyKey, Apps for AppKey+SigningSecret (one row per platform, click Reveal). For backend ingestion, mint a server key at API keys — that's a Bearer token (srv_…), not an HMAC pair.

install_uuid

Auto-generated by the SDK on first launch and stored in PlayerPrefs at key reflect.install_uuid. It's the SDK's primary key for a device and it's included in every event.

  • Format: 36-char UUID (8f2a1c0e-94d7-423b-8b53-af7c9e21d630) on Unity, 32-hex without dashes for S2S.
  • Survives: app updates, sessions, network restarts.
  • Doesn't survive: PlayerPrefs.DeleteAll(), app reinstalls (per OS policy), DeleteUserData().
  • Don't set manually from the SDK — read it via ReflectSDK.InstallUuid if you need it (e.g. to pair with S2S calls from your backend).
Debug.Log(ReflectSDK.InstallUuid);
// "8f2a1c0e94d7423b8b53af7c9e21d630"

It's the join key linking click → install → every subsequent event. See "FAQ" below.

event_id

Auto-generated per event by the SDK as a 32-character hex GUID (Guid.NewGuid().ToString("N")).

  • De-duplication: if a flaky network triggers the SDK's retry path, the same event_id arrives twice. The server's INSERT OR IGNORE dedupes — exactly-once in practice.
  • S2S: when calling /s2s/event from your backend, supplying your own event_id (16–64 chars) makes retries idempotent. Omit and Reflect generates one.
  • Don't set manually from the client SDK — read-only on the wire.

SetUserId — known users

Once you authenticate a user (login, sign-up, account link), call:

ReflectSDK.SetUserId("user_42");

// Read it back
Debug.Log(ReflectSDK.UserId);

// Sign-out → back to anonymous
ReflectSDK.SetUserId(null);

Subsequent events carry user_id: "user_42" in the payload.

install_uuid vs user_id — the two-identifier model

A common question: "If user A logs out and user B logs in on the same device, they share the same install_uuid — won't their events clash?"

No, because every event carries both identifiers, and reports group by whichever you need. This is how every MMP works (Adjust, AppsFlyer, Branch). Reflect follows the same model:

IdentifierBound toUse for
install_uuidThe device install — one per app installation, never changes.Attribution chains (click → install → first events), SKAN postbacks, uninstall tracking, fraud signals tied to the device.
user_idThe account — set when they log in, cleared on logout, can change many times per device.Per-user funnels, LTV, cross-device stitching, retention by user, segmentation.

The install_uuid staying the same across users is by design. Changing it per user would break attribution: an ad network that sent a click for that install would lose track of who's converting. SKAN postbacks would mismatch. Uninstall detection would fire false positives.

For per-user analytics, the right primitive is user_id. The SDK already stamps every event with both — you just need to call SetUserId at login and SetUserId(null) at logout:

// On login
ReflectSDK.SetUserId(currentUser.Id);

// On logout — back to anonymous; events from anonymous browsing
// (visits, tutorial taps) get user_id = null until next login.
ReflectSDK.SetUserId(null);

Server-side, every events row has both columns. Reports key off whichever makes sense for the question:

  • "How many installs from Meta last week?" → group by install_uuid
  • "Did user_42 reach level 5?" → group by user_id
  • "What's the LTV of user_42 across all their devices?" → group by user_id (joins across multiple install_uuids)
  • "Which device is making fraudulent clicks?" → group by install_uuid

You can see both fields in every row at Event Explorer and in the Users view (groups by user_id, shows linked installs).

Anonymous → known stitching

The first time you call SetUserId with a non-null value, the SDK auto-fires a single _user_alias event. The server writes a row into user_aliases mapping install_uuid → user_id. Reports can then JOIN on user_id to stitch pre- and post-login funnels.

// First launch — anonymous user does tutorial
ReflectStandardEvents.TutorialCompleted("intro");
// → event { install_uuid: "...", user_id: null, ... }

// Later — they sign up
ReflectSDK.SetUserId("user_42");
// → SDK auto-fires { event_name: "_user_alias", install_uuid: "...", user_id: "user_42" }
// → server writes user_aliases row

// Subsequent events
ReflectStandardEvents.LevelCompleted(1);
// → event { install_uuid: "...", user_id: "user_42", ... }

The stitch event is fired once per anonymous → known transition. Re-issuing the same user_id later is a no-op.

Device identifiers

IdentifierPlatformWhen collected
gaidAndroidIf limitAdTracking=false AND advertising consent granted
idfaiOSOnly after ATT prompt returns Authorized
idfviOSAlways (per-vendor, no consent needed)
android_id (SSAID)AndroidAlways

If RequireAdvertisingConsent=true in your config, GAID/IDFA are NOT collected until you call SetAdvertisingConsent(true). Recommended for EU/EEA/UK distribution.

iOS App Tracking Transparency (ATT)

// Auto-prompt on first launch (set in config)
new ReflectConfig {
    AutoRequestIosTracking = true,
    ...
}

// OR call manually when you have a good moment in your UX
ReflectSDK.RequestIosTracking(status => {
    if (status == IosTrackingStatus.Authorized) {
        // IDFA will now be on subsequent events
    }
});

The SDK automatically injects NSUserTrackingUsageDescription into your iOS Info.plist via ReflectBuildPostProcessor. Customize the wording in Editor/ReflectBuildPostProcessor.cs (AttUsageDescription field).

Audience tags

Apply segmentation tags so reports can filter by cohort:

ReflectSDK.SetAudience("paying", "whale_v3");

The SDK fires a _set_audience event with the tags array; the server upserts install_audiences rows (one per tag). Reuses the existing /event ingestion pipe — no extra Cloudflare requests.

FAQ

  • Same key for iOS and Android? Only CompanyKey. AppKey+SigningSecret differ per platform.
  • SDK URL? https://api.reflect.cloud.
  • Leaked SigningSecret? Rotate at Apps; ship new build.
  • Spoof risk? No — every request is HMAC-signed; CompanyKey+AppKey alone don't grant write access.
  • S2S identifiers? Generate install_uuid yourself, reuse per user. event_id optional but recommended for idempotent retries.