Identify users
Link the anonymous person to your user ID after sign-in, and set person attributes.
The SDK starts every install with an anonymous distinct ID, so purchases and events work before
sign-in. Call identify() once your user signs in, so their access follows them across devices.
How anonymous and identified people merge is explained in
People and identity.
Identify after sign-in
await voidhash.client.identify(user.id, {
email: user.email,
name: user.name,
});Pass a stable, opaque ID from your backend, such as a UUID, not an email address. Any non-blank
string works; a blank ID returns INVALID_ARGUMENT and leaves the identity unchanged.
identify() switches the identity on the device first, then links the previous anonymous ID on the
server. If Voidhash can't be reached, the link is stored and sent later, across restarts. A person
who bought before signing in keeps their cached access, marked stale, until the server merges them.
Use identifySync() to learn which happened: it resolves to confirmed with the person, or
deferred with the last known person.
If the anonymous ID already belongs to a different identified user, the device still switches to the
new ID, without merging the anonymous history, and onDiagnostic reports PERSON_WRITE_REJECTED.
Identity calls run in the order you make them, including during initialization. Repeating
identify() with the same ID and profile keeps the session and cache.
Follow your authentication state
Wait until your auth library has restored the session; a loading session isn't a signed-out one. Then identify a signed-in user, or sign out a user who was identified:
import { isAnonymousDistinctId } from "@voidhash/react-native";
import { voidhash } from "./voidhash";
export async function syncVoidhashIdentity(user: { id: string } | null) {
if (user) {
await voidhash.client.identify(user.id);
return;
}
const current = await voidhash.client.getDistinctId();
if (current.isOk() && current.value !== null && !isAnonymousDistinctId(current.value)) {
await voidhash.client.signOut();
}
}Don't sign out an anonymous person: signOut() always starts a new anonymous identity, which would
leave their purchases behind.
Sign out
Call signOut() after your own sign-out completes:
await auth.signOut();
await voidhash.client.signOut();signOut() records $sign_out, flushes events under the old identity, then starts a new anonymous
identity and analytics session, outside the user's groups. reset()
switches to a new anonymous identity, also without groups, without recording $sign_out. Switching
straight from one user to another with identify() doesn't merge them.
Set person attributes
await voidhash.client.setPersonAttributes({
email: "ada@example.com",
plan_source: "referral",
});email and name are strings stored in the person's built-in fields. Other keys are custom
attributes whose values are strings, numbers, booleans or null; anything else returns
INVALID_ARGUMENT and nothing is stored.
The update is stored on the device and sent in the background until Voidhash applies it, after any
pending identity link. setPersonAttributesSync() waits for the server and resolves to:
status | Meaning |
|---|---|
confirmed | Applied; person is the updated person. |
deferred | Queued; person is the last known person. |
skipped | Not recorded, because analytics consent isn't granted. |
disabled | The client was created with enabled: false. |
Without analytics consent, setPersonAttributes() stores nothing either. setPersonAttributesSync()
returns READ_ONLY_PURCHASE_NOT_ALLOWED in observer mode.
Read the distinct ID
const result = await voidhash.client.getDistinctId();
const distinctId = result.isOk() ? result.value : null; // null while disabledLog it in support tools, or send it to your backend to look the person up.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.