quotastack Docs
Docs / Concepts / Environments & the Shared Catalog

Environments & the Shared Catalog

What sandbox and live keep apart, what they share, and why editing a plan in sandbox can change a live customer's bill.

Mental model — Think of it as two warehouses that share one product catalogue. Sandbox and live each hold their own stock, customers, and paperwork — but there is a single price list on the wall, and both warehouses read it. Reprice an item while playing in sandbox and the live warehouse charges the new price.

Quick take

  • Two environments: live and sandbox
  • Customer data is separated — customers, balances, subscriptions, ledger, reservations, topups, webhook events
  • The catalog is NOT separated — plans, variants, billable metrics, metering rules, topup packages are shared
  • Editing a plan or a price in sandbox changes it for live customers too
  • The sandbox API key prefix is qs_test_; the environment is named sandbox
QS_TEST_ Sandbox Customers · BalancesSubscriptions · LedgerReservations · Topups QS_LIVE_ Live Customers · BalancesSubscriptions · LedgerReservations · Topups SHARED CATALOG Plans · Variants · Metrics Metering rules · Topup packages Editing a shared plan in sandbox changes what a LIVE customer is billed

Environments & the Shared Catalog

QuotaStack gives every tenant two environments: live and sandbox. Your customer data is kept separate between them. Your catalog is not. The second half of that sentence surprises people, so this page spells out both halves.

Choosing an environment

There are three ways to pick an environment. They all point at the same two places.

With an API key. The product API decides from the key prefix.

PrefixEnvironment
qs_live_...live
qs_test_...sandbox

In the dashboard. The header has a Sandbox / Live toggle. It changes which customer data you see.

With a header. Dashboard-session calls send X-Environment: sandbox or X-Environment: live.

One naming note. The sandbox API key prefix reads qs_test_, but the environment is named sandbox. They are the same place. The API, the webhook payloads, and the dashboard all use the word sandbox.

What is separate

Each of these rows is stored with the environment it belongs to. A row in one environment is invisible from the other.

DataWhat separation means
CustomersA customer created in sandbox does not exist in live.
Credit accounts and balancesSandbox credits are not spendable in live.
Credit blocksA block granted in sandbox never appears on a live balance.
Ledger entriesEach environment has its own transaction history.
ReservationsA sandbox hold never touches a live balance.
Topup purchasesA topup recorded in sandbox is a sandbox record only.
SubscriptionsA sandbox subscription bills nobody in live.
Overage eventsArrears are tracked per environment.
Gauge statesSeat counts and other gauges are counted per environment.
Webhook eventsThe delivery log is per environment.
Audit logEach environment has its own audit trail.

What is shared

These rows have no environment at all. There is one copy, and both environments read it.

CatalogShared
PlansYes
Plan variantsYes
Plan credit grantsYes
Variant entitlementsYes
Billable metricsYes
Metering rulesYes
Topup packagesYes

There is no sandbox copy of a plan. There is no live copy of a metering rule. There is one of each, and a change to it is a change everywhere.

What this means in practice

These are things that will happen, not things that might.

Retire a plan in sandbox and it is retired in live. Live customers subscribed to that plan are affected right away.

Change a metering rule’s price in sandbox and live customers are charged the new price. The rule is the same row. Saving it in sandbox saves it for everyone.

Create a billable metric in sandbox and it appears in live. There is no promotion step, because there is nothing to promote.

Here is a real example. A metric named tarot_reading was created in the live environment, with a metering rule of one credit per unit. A brand-new sandbox customer was then able to reserve seven units against tarot_reading immediately, and the hold cost seven credits from the sandbox balance. The customer was sandbox. The balance was sandbox. The metric and its price came from live, because there is only one of each.

Working safely

Treat every catalog write as a production change. It does not matter which environment you are in. If you are editing a plan, a variant, a metric, a metering rule, or a topup package, you are editing live.

Give experiments their own names. Use a metric key like test_tarot_reading rather than reusing a real one. A test plan should be obviously a test plan by its name, because both environments will show it.

To try a new price, add a new metering rule. Do not edit the live one. Rules have effective dates, so a new rule can take over without touching the old one.

Check the environment badge before you edit. It tells you which customer data you are looking at. It does not protect the catalog, because there is only one catalog.

One exception worth knowing

Subscription entitlement overrides do not carry an environment of their own. They still cannot leak between environments, because an override belongs to a subscription, and subscriptions are separated. An override created against a sandbox subscription affects that sandbox subscription only.

The rule is this. Catalog rows have no environment, and they are shared. Rows that hang off a customer or a subscription follow their parent, whether or not they store the environment themselves.

Common mistakes

Don't test a price change by editing a metering rule in sandbox

Metering rules are shared. The new price applies to live customers the moment you save it. Create a new rule instead.

Don't retire or rename a plan "just to see what happens" in sandbox

Plans are shared. Retiring one in sandbox retires it in live, where real subscribers are attached to it.

Don't assume the dashboard's environment toggle protects everything

It switches which customer data you see. It does not give you a second copy of the catalog — there is only one.