Every workspace token carries a set of scopes, fixed at mint time. A call
outside them returns 403 with code scope_denied — the token is valid, it
just is not allowed.
The four scopes
| Scope | Unlocks |
|---|---|
read |
Workspace info, account and wallet listing, addresses, assets, network list |
write |
Creating and deleting accounts, deriving and importing wallets, and tags |
sign |
Message, typed-data, digest, and bytes signing |
export |
The two sealed key-export routes, and nothing else |
Request them at mint time:
{
"workspace": "demo",
"password": "…",
"scopes": ["read", "sign"]
}Omit scopes entirely and you get ["read", "write", "sign"] — the default
grant. export is never included by default; it must always be asked for
by name.
An unrecognised scope name is rejected outright with 400 and
invalid_body — unknown scopes are never silently dropped. So is an empty
array: scopes must name at least one, and at most the four that exist.
Repeating a name is harmless, since the grant is de-duplicated.
Refreshing a token preserves the scopes it already had. There is no way to widen or narrow a live lease — mint a new token for a different grant.
Tiers for scoped API keys
A scoped API key — the durable key a tenant mints for an end user — is granted not as a free scope list but as one of two fixed tiers:
| Tier | Scopes | What a scoped key can reach (this release) |
|---|---|---|
basic (default) |
read, write |
Reading its bound account, wallets, and addresses |
unlimited |
read, write, sign, export |
The above, plus sealed export (with a live inquiry key) |
The workspace-token scope list above still applies to workspace tokens; tiers
are how the scoped-key mint chooses between the same underlying scopes. Note the
write and sign scopes ride on a scoped key’s lease but are not reachable by
it: a scoped key is confined to its bound account’s read routes plus export, so
signing and account/wallet management stay with a workspace token.
Why export is different
A leaked signing token can sign anything the key is entitled to sign — including a transaction its holder then broadcasts — and leaves a trace. A leaked export hands over permanent, offline, irrevocable control of the key itself. So export is gated three independent ways, and all three must hold:
The Unlimited tier
Among scoped-key tiers, only Unlimited carries the export scope, so a Basic
key is refused at the export route with scope_denied. (A workspace token can
also be granted export, but never reaches the route without a live inquiry
key — see below.)
A live inquiry key
The key must have been minted with the end user’s inquiry key, and its window must not have lapsed. Without it, export is locked — the key can do everything else it is entitled to, but not export.
Encryption to the end user’s own key
The response never contains plaintext key material. It contains a sealed blob encrypted to the inquiry key, so the operator — who never holds the matching private key — cannot open it either.
Every export attempt is audited — attempt, success, and failure alike — because “when did this key leave” has to be answerable after the fact.
Five scope-free routes
Five workspace-token routes accept any scope:
POST /v1/accounts/{slug}/unlockandPOST /v1/accounts/{slug}/lock. Unlocking is a precondition for work you are already authorised to do, not a privilege of its own — the scope check still applies to whatever you do next.POST /v1/auth/token/refreshandDELETE /v1/auth/token. Extending or ending your own session is not an operation on the workspace’s contents, so no scope gates it. A token can always refresh and revoke itself, whatever it was minted to do.GET /v1/auth/whoami. Introspecting your own token — reading the scopes, tier, and expiry it already carries — is not an operation on the workspace’s contents either, so no scope gates it.
Related
- Export key material — the full sealed-export flow
- Auth endpoints — minting with explicit scopes
- Errors —
scope_deniedand neighbours