Skip to main content
Every organization owns exactly two environments, live and test, seeded when the organization is created. They cannot be created or deleted, and there is never a third. Customer-plane tables carry environment_id alongside organization_id. Test data and live data are the same schema and never the same rows.

The credential decides

This is the rule the whole design turns on. On the machine surface, the environment is resolved from the stored key row on every request. Not from a header, not from a query parameter, not from a body field. There is no way to name one.
Which means:
  • a live id fetched with a test key answers 404 — and the 404 does not reveal that the row exists elsewhere;
  • a contact identified with a test key is unreachable from live;
  • pointing staging at production is not a mistake you can make in a config file, because there is no field to get wrong.
The environment kind is minted into the key’s prefix, so a key that leaks says on its face which mode it opens.
GET /v1/me is the cheapest way to confirm which environment a key opens. Every integration should call it once at start-up.

The panel is the exception

Staff are authorized for the whole organization, so panel reads take the environment as an explicit view parameter:
It defaults to live. Anything other than live or test answers 400 invalid_request. This is the opposite posture from the machine surface, and safe for exactly that reason: the caller is already authorized for both environments, so letting them choose adds no reach. A machine credential gets no such parameter, ever. In the panel UI the active environment is a client-side view preference, marked with the header switch and an amber banner whenever test mode is on.

Auth mode is per environment

Each environment carries its own auth_mode, hosted or federated. That is deliberate: you can evaluate hosted in test while production stays federated.
Requires organization:update. The endpoints of the other mode refuse rather than quietly become a second sign-in: See Choosing an auth mode.

The identity secret

A federated environment also holds an identity secret — the HMAC key your server signs external_id values with. It is per environment, minted on first read, and reading it requires api_keys:write: this secret mints verified sessions, so reading it is holding it.
Rotating replaces it and keeps the previous one verifying for a 24-hour grace window — long enough for a fleet to redeploy, short enough that a stolen old secret has a deadline.
Both endpoints answer 501 federated_identity_unavailable when the server cannot keep the secret encrypted safely.