Keys and environments
Sandbox and live, secret and publishable, and which mistake each one prevents.
Environments
Every project has a Sandbox and a Production environment. They are completely separate: separate customers, separate plans, separate invoices, separate number sequences. Nothing crosses between them.
A key belongs to exactly one environment, so the key decides which data you touch. There is no "environment" parameter to get wrong.
Two kinds of key
| Prefix | Where it belongs | What it can do |
|---|---|---|
b316_sk_… | Your server, only | Everything the API offers |
b316_pk_… | A browser | Public catalog and sign-in flows |
A publishable key cannot read customers, subscriptions or invoices. That is the point of it: it ships to a browser where anybody can read it, so it must not be able to enumerate your customers.
The band is in the key itself — b316_sk_sandbox_… versus b316_sk_live_… — so
you can tell what you are pointed at without asking.
The mistake this prevents
Running a script against production while believing it is sandbox.
Both the CLI and the MCP server read the band from the key and refuse writes on a live key unless you say so explicitly:
$ base316 usage cus_abc123 api_call
Refused: recording usage would change live data.
This key is a live key. Re-run with --live if you mean it, or export a sandbox
key instead.The check is local — it reads the key's own prefix rather than asking the server — so it cannot fail open when the network does.
Scoped keys
A secret key can be issued with permissions, so it can do one job and no other — a sales tool that sends agreements has no business reading invoices. Today the scope that exists is agreements:
| Permission | Allows |
|---|---|
agreement.read | GET /v1/agreements and everything under it |
agreement.write | Creating, sending, resending, revoking and importing |
agreement.* | Both |
A scoped key is refused, with KEY_PERMISSION_DENIED and the permission it
needed in error.param, on every route its permissions do not name — it is
fail-closed. A key issued without permissions is unrestricted, exactly as keys
always were. Publishable keys cannot be scoped. Choose the scope when you issue
the key under Keys.
Rotating a key
Create the new key first, deploy it, then revoke the old one. A revoked key stops working immediately, and its row is kept so audit history stays attributable rather than pointing at nothing.
Configuration
| Variable | Meaning |
|---|---|
BASE316_SECRET_KEY | Your server-side key |
BASE316_API_URL | Defaults to https://api.base316.com |
The SDK reads both from the environment, so new Base316() with no arguments is
usually right.

