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, tags, setting RPC URLs |
sign |
Message signing, typed-data signing, and all transaction routes |
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.
Why export is different
A leaked signing token moves funds within whatever policy you have set, 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:
A distinct scope
export must be named explicitly in the mint request. No default grant
includes it.
A tenant-level enable
Your operator must have registered an exportPublicKey for the tenant. If they
have not, the mint itself fails with 403 and export_disabled — you cannot
even hold an export-scoped token.
Encryption to an operator-held key
The response never contains plaintext key material. It contains a sealed blob encrypted to the registered X25519 public key, so a stolen token yields something the thief cannot open.
Every export attempt is audited — attempt, success, and failure alike — because “when did this key leave” has to be answerable after the fact.
Four scope-free routes
Four 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.
Related
- Export key material — the full sealed-export flow
- Auth endpoints — minting with explicit scopes
- Errors —
scope_denied,export_disabled, and neighbours