Check access
Gate features on the current person's active perks.
Check access by asking whether the current person has an active perk, such as premium. The
person's entitlements list each perk they can use and what granted it; see
Products and perks and the
subscription lifecycle for when access starts and ends.
The SDK decides what your app shows. It can't protect anything a person reaches through your own API: check access on your server for that, with the server library for Node.js, PHP, Go or Rust, or over HTTP.
Gate a feature
hasActivePerk takes the perk's slug from Studio:
suspend fun hasPremium(): Boolean =
voidhash.getCurrentPerson()?.hasActivePerk("premium") == trueactivePerkSlugs lists every perk the person can use right now.
A subscription status doesn't say which features a product unlocks, and misses access from one-time purchases. Active perks cover both.
Caching and offline behavior
getCurrentPerson() answers from a cache and refreshes behind the read. The snapshot is fresh for
5 minutes and kept for 2 days; after that it's still served, marked expired. With nothing cached,
the call waits at most 10 seconds for Voidhash. The SDK refreshes the person after purchases,
restores, identify and reset. Pass forceFetch = true to wait for a fresh answer:
val person = voidhash.getCurrentPerson(forceFetch = true)getCurrentPerson() never throws because Voidhash is unreachable. It keeps serving the last
snapshot until Voidhash confirms the person doesn't exist. Use getCurrentPersonState() when you
need to know how current the answer is:
val state = voidhash.getCurrentPersonState()
val hasPremium = state.value?.hasActivePerk("premium") == true
if (state.value == null && state.isExpired) {
// Unknown: nothing is cached and Voidhash couldn't be reached. Don't lock by default.
}| Field | Meaning |
|---|---|
value | The person, or null. |
isStale | Past the 5-minute refresh window; a refresh is under way. |
isExpired | Past the 2-day lifetime, or nothing cached and no answer. Still served. |
A null person that isn't expired is a confirmed answer: Voidhash has no person for this identity
yet, such as an anonymous visitor who hasn't bought anything.
Read the person
VoidhashPerson holds the person's identity and entitlements:
| Property | Meaning |
|---|---|
distinctId | The current distinct ID. |
personId | Voidhash's ID for the person. |
email, name | Set through identify or person attributes. |
entitlementGrants | Every grant, active or expired. |
currentSubscription | The current subscription: productId, status (active, trialing or canceled) and expiresAt, or null. See Statuses. |
raw | The full JSON snapshot, including purchase history. |
Each EntitlementGrant has these fields:
| Field | Meaning |
|---|---|
perkSlug | The perk's slug in Studio. Compare this one. |
perkId | The perk's internal ID. |
status | active or expired. isActive is true for active. |
source | What granted it: subscription or purchase. |
sourceId | The subscription or purchase that granted it. |
expiresAt | When access ends, as an ISO 8601 string, or null for access that doesn't expire. |
Use currentSubscription for account screens, such as the current plan and its renewal date.
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 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 debug builds and the production key in release builds.
Next steps
- Identify users so access follows them across devices.
- Make and restore purchases.
- Check access from your backend with Node.js, PHP, Go, Rust or over HTTP
- Troubleshooting.
Something wrong or missing on this page? Tell us by email or on Discord.