Authentication
Bearer Tokens
Section titled “Bearer Tokens”Every route except /health, /api/v1/openapi.json, /api/v1/docs and
/api/v1/token requires a bearer token.
Authorization: Bearer <token>Tokens are held in the operating system keychain, with a label, a scope and an optional lifetime. This documentation does not describe the shape of a token — treat it as an opaque string, and store it the way you store any other secret.
Getting One
Section titled “Getting One”From the command line:
tcc config create-api-token --scope fullOr over HTTP, from the machine itself:
POST /api/v1/token{ "name": "my-integration", "scope": "full" }That route takes no token of its own. It is safe only because the server binds to loopback, so anything that can reach it is already on the machine. The answer carries the token and its scope.
Over HTTP that answer is the only time the token is shown:
GET /api/v1/api-tokens lists them masked. The command line can still reveal a
stored one to the person at the machine, which is why the keychain is the
boundary that matters.
GET and POST on /api/v1/api-tokens list and mint tokens;
DELETE /api/v1/api-tokens/{id} revokes one.
The Three Scopes
Section titled “The Three Scopes”A token carries one of three scopes. Scope is enforced centrally, from the HTTP method and the path, before a handler runs.
| Scope | May do |
|---|---|
read | Any GET outside the management routes |
write | The same, plus writes to your own data |
full | Everything, including organisation management and token management |
Management means /api/v1/org/members, /api/v1/org/groups,
/api/v1/org/alerts and /api/v1/api-tokens. Those need full on every
method, reads included. Nothing else needs full — the rest of the organisation
routes, activity and analytics among them, are ordinary reads.
Give an integration the narrowest scope that does its job. A reporting script
wants read; it will then be refused if it ever tries to delete something.
Refusals
Section titled “Refusals”A missing or invalid token is 401. A valid token whose scope is too narrow is
403, with a body naming the scope you have, the request you made and the scope
it would need:
{ "error": "insufficient_scope", "message": "Token scope 'read' cannot perform DELETE /api/v1/tags/abc. Requires write or full scope.", "required_scope": "write or full"}Revoking a token is immediate: delete it through /api/v1/api-tokens/{id} and
the next request carrying it is refused.