A workspace is a password-protected collection of accounts. At rest it is locked, and its contents are unreadable without the password.
A token is the unlock
There is no separate unlock endpoint. POST /v1/auth/token takes the workspace
password and returns a bearer token, and that token’s lifetime is the unlock
session. While a valid token exists, the workspace is open. Revoke it and the
workspace locks again.
This has three consequences worth internalising:
- The workspace password is presented exactly once per session, at mint time. That call is authenticated by tenant credentials, because it is the only workspace-tier call that has no token yet.
DELETE /v1/auth/tokenrevokes the lease of the token that sent it, and only that one. Other tokens on the same workspace keep the session open; it locks when the last lease goes. Minting twice for one workspace gives you two independently revocable tokens, not two copies of one.- No workspace-token route takes the workspace in its path. It comes from the token’s claim, so a path and a token can never disagree about which workspace is in play. The tenant-tier routes do use a path slug — they carry no token to disagree with.
Two clocks
A session is bounded by two independent timers, both configured per tenant.
| Clock | Config | Default | Meaning |
|---|---|---|---|
| Idle | ttl.workspaceIdleSec |
15 minutes | Time since the last activity on the session. Any authenticated request resets it, not only a refresh. |
| Absolute | ttl.workspaceAbsoluteSec |
8 hours | Total session lifetime from mint. Refresh does not extend it. |
Your operator can set both per tenant, so treat the defaults as the shape of the thing rather than a contract — but they are what applies when nothing is configured.
POST /v1/auth/token/refresh issues a new token and pushes the idle clock out.
Its response reports both, so you can see the ceiling coming:
{
"token": "eyJhbGciOi...",
"expiresAt": "2026-08-18T12:49:56.000Z",
"sessionExpiresAt": "2026-08-18T20:15:00.000Z",
"workspace": "demo",
"scopes": ["read", "write", "sign"]
}When sessionExpiresAt arrives, refreshing stops working. Mint a new token with
the workspace password instead.
Accounts have their own lock
Locking is two-tiered. Opening the workspace does not necessarily open everything inside it.
- An account using the shared workspace password unlocks transparently the
first time you touch it — but only for as long as its custody window lasts.
When that window closes the account locks like any other, and reopening it
means calling
POST /v1/accounts/{slug}/unlockwith the workspace password. - An account created with
hasOwnPassword: truehas its own password and must be unlocked explicitly viaPOST /v1/accounts/{slug}/unlock. Until then it answers423with codeaccount_locked.
Account unlocks have their own absolute timer (ttl.accountAbsoluteSec,
typically much shorter than the session) and their own rate limit — repeated
failed attempts return 429 with account_unlock_rate_limited, backing off
1, 2, 4, then 8 seconds.
The fifth consecutive failure ends the session. Not just that unlock — the
whole workspace session is destroyed, and every other token holding it open
starts answering 401 with session_expired. Treat a wrong account password
as a stop condition, not something to retry into.
Capacity
Four limits can refuse a mint even when your credentials are perfect.
Three of them share the code session_capacity_exceeded, and error.details
names which one you hit:
details.scope |
Ceiling | Default |
|---|---|---|
workspace |
Live tokens on this one workspace | 64 |
tenant |
Workspaces your tenant holds open at once (limits.maxUnlockedWorkspaces) |
8 |
process |
Workspaces open across the whole service, every tenant | 32 |
The process leg is worth knowing about because you cannot fix it: it is not
your tenant’s activity that filled it, and revoking your own tokens may not
clear it. The workspace leg surprises clients that mint per request instead
of reusing a token — sixty-four live leases on one workspace is enough, with no
second workspace open at all.
The fourth is the mint rate limit — 429 with mint_rate_limited, per tenant.
The charge applies once the request is well formed and names a known workspace,
so a tight retry loop against a bad password is counted.
Related
- Authenticate — the token lifecycle as a task
- Scopes and permissions — what a token is allowed to do
- Auth endpoints — exact request and response shapes