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.
Device identifiers Reflect collects
Used for deterministic + probabilistic attribution. All advertising/device ids are consent-gated (see Privacy) and scrubbed server-side on consent denial.
- Android: GAID (+ source/retry), Google App Set ID (Android 12+), Android ID (SSAID), and — China opt-in only — Fire ID, IMEI/MEID, OAID.
- iOS: IDFA (post-ATT) and IDFV.
- Push token (FCM/APNS) — set it with
ReflectSDK.SetPushToken(token)for re-engagement attribution. - External device id — your own backend's id, set with
ReflectSDK.SetExternalDeviceId(id), attached to every event so you can join Reflect data to your records.
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.
setEmail — attribute installs & revenue to an email
To tie an install (and the revenue that follows it) back to a known email — e.g. to attribute it to a lifecycle email send — tell the SDK the user's email. The SDK then attaches it as props.email on the app_install event and on every subsequent event, which is what email attribution matches against. See Privacy & consent for the consent gate and hashing details.
// Flutter
await Reflect.setEmail("user@example.com"); // pass the RAW email
await Reflect.clearEmail(); // on logout
// React Native
Reflect.setEmail("user@example.com");
Reflect.clearEmail();
// Web
ReflectWeb.setEmail("user@example.com");
ReflectWeb.clearEmail();Call setEmail with the plaintext address; Reflect's server hashes it (sha256) before storage and before forwarding to ad-network CAPIs. Don't hash it yourself. It is consent-gated: the sticky email is attached to events only while consent is not denied — deny consent (or callDeleteUserData) and it is dropped from every event. Available in the Flutter, Web, and React Native SDKs v1.2.0.
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.