Authentication
Bearer keys, scopes, world and character bindings, and how revocation lands.
Keys
Customer requests send a bearer key in the Authorization header. Keys use the trr_ prefix. The secret is shown exactly once at mint time in the console; the server stores a SHA-256 of the secret and cannot re-display it.
Authorization: Bearer trr_…Keys are org-bound. The organization is fixed on the credential; a key never acts for another org. Mint and revoke from the console under the org you mean to use.
Scopes
A key carries one or more scopes. The five scopes and what each grants:
| Scope | Grants |
|---|---|
define | Create and register scenario definitions |
seat | Take over or add characters; configure brain, memory, and prompts |
observe | Read worlds, characters, perceptions, and summaries |
measure | Read usage and spend; without it, usage fields read as absent |
control | Advance and pause worlds; mint and revoke keys |
The console ships two presets: full access (all five) and read only (observe + measure). Usage fields on a world are None when the key lacks measure; that means missing scope, not zero spend.
Bindings
A key may be narrowed to specific worlds and characters via world_ids and character_ids at mint time. A binding restricts what the key can reach; it never widens a scope the key does not hold. An unbound key can reach every world and character its org owns, within its scopes.
Secret and publishable keys
A key is secret by default: it belongs on a server and never in a browser. A publishable key, minted with kind="publishable" and the prefix trr_pk_, holds exactly the observe scope and reads only worlds that have been published. It is safe to embed in client-side code; everything else still takes a secret key.
Revocation
Revoking a key in the console takes effect immediately for new requests. Allow up to 30 seconds for running services to notice and drop a cached credential. There is no undo; mint a new key if you need the same scopes again.
Using the key
The client resolves the key as argument, then environment, then config file: Client(api_key="trr_..."), then TERRARIUM_API_KEY, then the file written by terrarium configure. Every request sends it as Authorization: Bearer trr_....
Getting 401 or 403
A 401 means the key itself: missing, mistyped, or revoked. A 403 means the key works but may not do this: a scope it was not granted, a world or character it is not bound to, or an org mismatch. Check what a key actually holds with client.me() or terrarium me; it reports the org, scopes, and bindings the server sees.
Base URL
Production API base: https://api.demosyne.com/v1. The SDK resolves the base as argument, then TERRARIUM_BASE_URL, then the config file written by terrarium configure, then that default.