VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
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

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:

statusMeaning
confirmedApplied; person is the updated person.
deferredQueued; person is the last known person.
skippedNot recorded, because analytics consent isn't granted.
disabledThe 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 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

On this page

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