Check access
Gate features using the current customer's active perk grants.
The current person snapshot contains entitlements, subscription state, and purchase history. Use entitlement grants to decide what the customer can access.
This page covers checks inside the app. Anything a customer could reach by calling your API directly must also be checked on the server — see Check access from your backend.
Gate a feature
A perk is available when its grant is active. Use the built-in useHasPerk hook to check whether
the current customer holds an active grant for a perk:
const { hasAccess, isLoading } = voidhash.useHasPerk("premium");
if (isLoading) return null;
return hasAccess ? <PremiumContent /> : <UpgradePrompt />;The hook returns:
| Field | Meaning |
|---|---|
hasAccess | true when the person holds an active grant for the perk. |
grant | The active grant behind hasAccess, or null. |
isLoading | true until the first snapshot has loaded. |
isStale | true when hasAccess was served from cache because a refresh failed. |
error | The refresh error, if any. |
refetch | Force a refresh. |
Outside React, use the imperative counterpart:
const { hasAccess, isStale } = await voidhash.client.hasPerk("premium");Gate on perks, not subscription status
Subscription status cannot tell you which features a product unlocks, and it misses access from one-time purchases or manual grants. Active perk grants are the access-control source of truth.
Offline and failure behavior
A failed refresh (offline, server error) is not proof that the customer has no access.
Both check APIs fail open with known-good data:
- If a cached snapshot shows an active grant,
hasAccessstaystrueandisStaleis set. - If there is no cached evidence of access,
client.hasPerkfails with the refresh error instead of answering a confident "no"; the hook reportshasAccess: falsewitherrorset.
Choose a fallback appropriate for each feature:
const { hasAccess, isStale, error, refetch } = voidhash.useHasPerk("premium");
if (isStale) {
// Cached access while offline — usually fine to keep serving content.
}
if (!hasAccess && error) {
return <RetryScreen onRetry={refetch} />;
}
return hasAccess ? <PremiumContent /> : <UpgradePrompt />;To require fresh confirmation — for example before unlocking a consumable — pass
{ allowStale: false } to the imperative check:
const result = await voidhash.client.hasPerk("premium", { allowStale: false });
if (result.isErr()) {
// Could not confirm access right now.
}Read the current person
When you need more than a single perk check, read the full snapshot:
const { data: person, error, isLoading, refetch } = voidhash.useCurrentPerson();data is the person snapshot, or null until the first snapshot loads or while the client is
disabled. See the SDK reference for the full shape.
For advanced cases you can still filter grants directly — prefer useHasPerk for gating:
const premiumGrant = person?.entitlements.grants.find(
(grant) => grant.perkId === "premium" && grant.status === "active",
);Grant fields
| Field | Meaning |
|---|---|
perkId | The perk slug configured in Studio. |
status | active or expired. |
source | subscription, purchase, or manual. |
sourceId | The subscription, purchase, or manual grant that created it. |
expiresAt | Expiration time, or null for access without an expiry. |
Use subscriptions.current for account UI such as the current plan or renewal state. Use
purchases.history when you need to show past transactions.
Refresh behavior
The SDK refreshes the person after purchases, restores, and identity changes. Reads use a stale-while-revalidate cache: a snapshot younger than five minutes is served immediately while a fresh copy is fetched in the background; older snapshots trigger a network fetch first. To force a network round-trip:
await voidhash.client.getCurrentPerson({ forceFetch: true });
// Or re-run the hook's request:
refetch();