Teams, projects and environments
How Voidhash organizes your apps, and how test purchases stay apart from production.
A team is your company's account in Voidhash, and a project is one app or product inside
it. A project owns its API keys, products, perks, paywalls, people and analytics. Inside a project,
every purchase and event belongs to an environment: production, sandbox or test.
The API and the CLI's org command call a team an organization.
Create a team and a project
The first time you sign in, Studio asks for a team name (3 to 32 characters) and a first project (up to 32 characters, with its platforms). Add more projects from the project switcher. See Get around Studio for the walkthrough.
Give each app its own project, and keep the iOS, Android and web versions of one app in the same project, so one person's access and analytics cover every platform.
Project settings
Under Settings → General, Studio shows the Project ID the API and CLI use, and holds:
- Simulated purchases: lets debug builds in development mode buy without a store. It is on for new projects. Deleting simulated purchases leaves production, sandbox, Stripe test mode and payment source test purchases in place.
- Tracking domain: a first-party domain for the web SDK, on Voidhash Cloud only. See Tracking domain.
- Delete project: deletes the project and all of its data, for admins and owners only.
Deleting a project permanently deletes all of its data. To confirm, you type the project's
team/project path.
Three settings are not in Studio. Change them with PATCH /api/v1/projects/{projectId}/settings,
the MCP tool update_project_settings, or the CLI (voidhash settings set):
| API field (CLI key) | Values | Default | What it controls |
|---|---|---|---|
storeSandbox.isUnlockingProduction (store-sandbox-unlocks-production) | true, false (on, off) | true | Whether store sandbox purchases unlock access in production apps. See Store sandbox in production apps. |
paidAccessSharing.production (paid-access-sharing.production) | shared, transfer, disabled | transfer | What happens when someone restores a purchase that another account of your app made. |
paidAccessSharing.sandbox (paid-access-sharing.sandbox) | shared, transfer, disabled | transfer | The same, for store sandbox and test purchases. |
The paid access sharing modes:
shared: both people keep access.transfer: access moves to the person who restored the purchase.disabled: access stays with the person who bought it.
A change applies to purchases reported from then on. The CLI asks for confirmation before it changes a setting that affects production apps.
curl -X PATCH "https://api.voidhash.com/api/v1/projects/$VOIDHASH_PROJECT_ID/settings" \
-H "Authorization: Bearer $VOIDHASH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"storeSandbox": {"isUnlockingProduction": false}}'voidhash settings set store-sandbox-unlocks-production offEnvironments
| Environment | What it holds |
|---|---|
production | Real purchases from the App Store, Google Play, Stripe live mode and payment sources. |
sandbox | Free purchases made in a store's own sandbox: App Store sandbox accounts, TestFlight, App Review and Google Play test purchases. |
test | Free purchases from your own builds and servers: simulated purchases in development mode, Stripe test mode, and payment sources reporting with a test key. |
The key a request uses decides the environment it reads and writes. Every API key belongs to production or test for its whole life:
| Key | Reads | Records events and reports as |
|---|---|---|
Production (vh_pk_…, vh_sk_…) | Production purchases, plus store sandbox purchases while they unlock production apps | production |
Test (vh_pk_test_…, vh_sk_test_…) | Store sandbox and test purchases | test |
No request header changes what a key reads. See API keys for how to create and use each key.
Store sandbox in production apps
The store, not your app, decides whether a purchase is a sandbox one. TestFlight runs the build you ship, and App Review buys with a sandbox account in the build you submit. So by default, store sandbox purchases unlock access in apps that use the production key, and App Review can open what it buys.
Set storeSandbox.isUnlockingProduction to false to stop this (in the MCP tool,
storeSandboxUnlocksProduction; in the CLI, store-sandbox-unlocks-production off). App Review then no longer unlocks what it
buys, and TestFlight testers need a build with the test publishable key. Test purchases never unlock
production, whatever this setting says.
Development mode
Development mode lets a debug build buy from a simulated store, for example on an emulator. It is
available in the Swift, Kotlin and
React Native SDKs. Debug builds in development mode must use
the test publishable key: Voidhash refuses simulated purchases made with a production key. Their
purchases are recorded as test.
Simulated purchases need Enable simulated purchases on under Settings → General → Simulated
purchases, which it is by default (voidhash settings set sandbox-purchasing on). Voidhash
processes them for real, so perks, webhooks and analytics behave as in production.
Simulate renewals, expiry and refunds
Move a person's simulated purchases through their lifecycle without waiting for a store. Each action changes the person's access at once; webhooks and analytics follow in the background.
| Action | Applies to | Effect |
|---|---|---|
renew | Subscription | Renews it for another period. |
expire | Subscription | Ends it now. |
grace_period | Subscription | Starts a 3-day billing grace period, as after a failed renewal. |
refund | One-time purchase | Refunds it; its access ends. |
revoke | Either | Revokes it, as when the store takes it back. |
Ask your agent, for example "Renew user_123's simulated pro_monthly subscription". It uses
get_sandbox_purchases to find the purchase, simulate_sandbox_event to play the action and
reset_sandbox to start over. See People tools.
Read the person's simulated purchases to get the subscription or purchase ID, then post an action
with a secret key. Repeating an actionId applies the action once.
curl "https://api.voidhash.com/api/v1/projects/$PROJECT_ID/sandbox/state?personId=$PERSON_ID" \
-H "Authorization: Bearer $VOIDHASH_SECRET_KEY"
curl -X POST "https://api.voidhash.com/api/v1/projects/$PROJECT_ID/sandbox/actions" \
-H "Authorization: Bearer $VOIDHASH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "actionId": "renew-1", "action": "renew", "targetType": "subscription", "targetId": "sub_..." }'DELETE /api/v1/projects/{projectId}/sandbox/data deletes every simulated purchase.
voidhash sandbox view user_123
voidhash sandbox renew user_123 pro_monthly
voidhash sandbox grace-period user_123
voidhash sandbox resetreset asks first; --yes skips that. Name the product when the person has several. The CLI
isn't published yet; see Install the CLI.
Delete simulated purchases under Settings → General also resets them. Resetting never touches production, store sandbox, Stripe test mode or payment-source test purchases, and the actions work only on simulated purchases: store sandbox and Stripe test purchases follow their store.
Analytics and webhooks
Voidhash stamps every analytics event with the environment of the key that sent it. Revenue events take the environment of their purchase. A staging website on the test key therefore stays out of your production visitors, events and experiment results.
People are not split by environment. If staging identifies users with the same IDs as production,
both reach the same people, so give staging users IDs of their own, such as a staging: prefix.
Webhook endpoints also belong to production or test; see Environments.
Switch environments in Studio
Studio shows one environment at a time. Open the project switcher and choose Production, Sandbox or Test. Purchases and revenue follow the switch. The Production and Sandbox views show production events, since store sandbox purchases come from apps that use the production key; Test shows test events.
A typical setup
| Where it runs | Key | Webhook endpoint |
|---|---|---|
| App Store and Google Play release builds, including TestFlight and Play test tracks | Production publishable key | — |
| Production website and backend | Production publishable and secret keys | Production |
| Staging website and backend, Stripe test mode | Test publishable and secret keys | Test |
| Debug builds in development mode | Test publishable key | Test |
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.