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.
| Credential | Per-app? | Per-platform? | Secret? |
|---|---|---|---|
CompanyKey (co_live_…) | No — one per company | No — same iOS & Android | No — public |
AppKey (app_live_…) | Yes | Yes — different iOS vs Android | No — public |
SigningSecret | Yes — paired with AppKey | Yes — different iOS vs Android | Yes — never commit |
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.InstallUuidif 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_idarrives twice. The server'sINSERT OR IGNOREdedupes — exactly-once in practice. - S2S: when calling
/s2s/eventfrom your backend, supplying your ownevent_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:
| Identifier | Bound to | Use for |
|---|---|---|
install_uuid | The 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_id | The 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 multipleinstall_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
| Identifier | Platform | When collected |
|---|---|---|
gaid | Android | If limitAdTracking=false AND advertising consent granted |
idfa | iOS | Only after ATT prompt returns Authorized |
idfv | iOS | Always (per-vendor, no consent needed) |
android_id (SSAID) | Android | Always |
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+SigningSecretdiffer 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+AppKeyalone don't grant write access. - S2S identifiers? Generate
install_uuidyourself, reuse per user.event_idoptional but recommended for idempotent retries.