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, email and name sends nothing and keeps the session and cache; a
changed email or name is sent.
Follow your authentication state
Wait until your auth library has restored the session; a loading session isn't a signed-out one.
Then tell the SDK what you found, on every launch and whenever the session changes: identify()
when there is a user, signOut() when there is none.
import { voidhash } from "./voidhash";
export async function syncVoidhashIdentity(user: { id: string } | null) {
if (user) {
await voidhash.client.identify(user.id);
} else {
await voidhash.client.signOut();
}
}You don't need to remember what you called before. A repeated identify() sends nothing, and
signOut() does nothing when nobody is signed in, so an anonymous person keeps the identity that
holds their purchases. If your users belong to companies or workspaces, pass the full set to
setGroups() in the same place.
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. When nobody
is signed in it does nothing.
reset() does exactly the same for a signed-in user. The two differ only when nobody is signed
in: signOut() does nothing, and reset() starts a fresh anonymous person, with a new ID, no groups
and a new session, without recording $sign_out. Use reset() on a shared device, for a guest's
"start over", and in tests.
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 that set the person's own email and name, as identify() does.
Other keys are custom attributes whose values are strings, numbers, booleans or null; anything
else returns INVALID_ARGUMENT and nothing is stored. Attribute calls wait for init() and keep
their order with the identity calls, so one made right after an identify() you didn't await
still applies to that user.
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 | Nothing was left to write: the only attribute was an agreement to ad data sharing, which needs analytics consent. |
disabled | The client was created with enabled: false. |
Person attributes are sent whatever the analytics consent,
as identify() is, including a changed email or name for the user who is already signed in.
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.