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

Check access

Gate features on the current person's perks, online and offline.

Gate features on perks: a person has access when they hold an active entitlement for the perk's slug. Perks come from the products the person bought, so one check covers subscriptions and one-time purchases. See Products and perks.

The SDK decides what the app shows. Protect anything reachable through your own API on your server, with the server library for Node.js, PHP, Go or Rust, or over HTTP.

Gate a feature

import { voidhash } from "../src/lib/voidhash";

export function PremiumGate() {
  const { hasAccess, isLoading } = voidhash.useHasPerk("premium");

  if (isLoading) return null;
  return hasAccess ? <PremiumContent /> : <UpgradePrompt />;
}
FieldMeaning
hasAccesstrue when the person holds an active entitlement for the perk.
grantThe entitlement behind hasAccess, or null.
isLoadingtrue while the SDK initializes and until the first check answers.
isStaletrue while the answer hasn't been confirmed by the server recently, or nothing is cached.
errorSet only when there was nothing to answer from, such as AUTHENTICATION_FAILED.
refetchChecks again.

The hook re-renders when a refresh lands, including after a purchase or restore, and checks again for the new person after identify() or reset().

Outside React, client.hasPerk() resolves to a Result:

const result = await voidhash.client.hasPerk("premium");
if (result.isOk() && result.value.hasAccess) unlock();

Offline and stale answers

A failed refresh isn't proof that the person has no access, so neither check fails because of the network:

  • Cached access stays true while the device is offline, with isStale set.
  • With nothing cached, hasAccess is false and isStale is true: there is no evidence either way.
  • If the publishable key was rejected and nothing is cached, the check fails once with AUTHENTICATION_FAILED.

Treat an unconfirmed "no" differently from a denial:

const { hasAccess, isLoading, isStale, refetch } = voidhash.useHasPerk("premium");

if (isLoading) return null;
if (hasAccess) return <PremiumContent />;
if (isStale) return <AccessUnconfirmed onRetry={refetch} />;
return <UpgradePrompt />;

client.hasPerk() also says how fresh its answer is:

FieldMeaning
isStaleThe cached answer is older than 5 minutes.
isExpiredThe cached answer is older than 2 days. Access from it is a best guess.
reasonfresh: confirmed by the server. refresh-in-flight: from cache while a refresh runs. refresh-failed: from cache because the refresh failed. no-cache: nothing seen yet for this person.

Before something you can't take back, such as granting a consumable, force a server check and act only on a confirmed answer:

const result = await voidhash.client.hasPerk("premium", { forceFetch: true });
if (result.isOk() && result.value.reason === "fresh" && result.value.hasAccess) {
  grantCoins();
}

Read the current person

For account screens, read the whole person:

const { data: person, error, isLoading, refetch } = voidhash.useCurrentPerson();

data is null until the first snapshot loads and while the client is disabled. client.getCurrentPerson() resolves to { person, isStale, isExpired, reason }.

FieldContents
personId, distinctIdThe person's IDs. See People and identity.
email, nameProfile fields, or null.
entitlements.grantsEntitlements, described below.
subscriptions.current{ productId, status, subscriptionId, expiresAt } or null. status is active, trialing or canceled; see Statuses.
subscriptions.historyEvery subscription, with startsAt, expiresAt, canceledAt, isTrial and status (active, trialing, canceled or expired).
purchases.historyEvery purchase, with productId, type (one_time or subscription) and createdAt.

Dates are ISO 8601 strings. Use subscriptions.current for plan and renewal UI, and perks for access. Subscription states are explained in Subscription lifecycle.

Each entry in entitlements.grants has:

FieldMeaning
perkSlugThe perk's slug. null when the perk was deleted.
perkIdThe perk's internal ID.
statusactive or expired.
sourcesubscription or purchase.
sourceIdThe subscription or purchase that created it.
expiresAtWhen access ends, or null for access that doesn't expire.

Refresh behavior

The SDK refreshes the person at launch, on foreground, after purchases and restores, and when the identity changes. Between refreshes, a snapshot younger than 5 minutes answers without a request; an older one answers from cache if a refresh takes longer than 500 ms. To refresh now, call refetch() or getCurrentPerson({ forceFetch: true }). resetCache() clears the cached person.

Environments

The publishable key the client uses decides which purchases unlock perks. With the production key (vh_pk_…), production purchases count, plus store sandbox purchases such as TestFlight and Google Play test purchases while they unlock production apps. With the test key (vh_pk_test_…), store sandbox, simulated and test mode purchases count, never production ones. Use the test key in development builds and the production key in release builds.

Next steps

  • Check access from your backend with Node.js, PHP, Go, Rust or over HTTP
  • Identify signed-in users
  • Troubleshoot a purchase that doesn't unlock

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

PreviousMake and restore purchasesNextIdentify users

On this page

Gate a featureOffline and stale answersRead the current personRefresh behaviorEnvironmentsNext steps