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.

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("[email protected]");   // pass the RAW email
await Reflect.clearEmail();                     // on logout

// React Native
Reflect.setEmail("[email protected]");
Reflect.clearEmail();

// Web
ReflectWeb.setEmail("[email protected]");
ReflectWeb.clearEmail();
Pass the raw email — the server hashes it

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:

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.