VoidhashDocs
DevelopersHelp centerGuidesAPI reference
voidhash.comOpen Studio
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Groups
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
React Native SDK
React Native quickstart
Display a paywall
Make and restore purchases
Check access
Identify users
Evaluate feature flags
Capture analytics
Groups
Track screens
Development mode
React Native SDK reference
Errors
Troubleshooting
Theme

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:

statusMeaning
confirmedApplied; person is the updated person.
deferredQueued; person is the last known person.
skippedNothing was left to write: the only attribute was an agreement to ad data sharing, which needs analytics consent.
disabledThe 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 disabled

Log it in support tools, or send it to your backend to look the person up.

Next steps

  • Check the identified person's access
  • Capture events
  • People and identity

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousCheck accessNextEvaluate feature flags
© 2026 Voidhash s.r.o.
voidhash.comPricingTermsPrivacyDiscord

On this page

Identify after sign-inFollow your authentication stateSign outSet person attributesRead the distinct IDNext steps