Tokens API
Mint, list, and revoke personal access tokens, and manage the MCP clients authorized against your account.
The Tokens API manages the credentials that authenticate you to Alaf Server: personal access tokens (PATs)
for the CLI and direct API calls, and the MCP client bindings created when an AI assistant connects over
OAuth. In the dashboard this is the Settings → Access area; from the terminal it's the
alaf server token command. Every route is self-scoped — you only ever see and manage your
own tokens and clients.
Base path & auth
All paths are relative to your instance, under /api — e.g. https://your-host/api/tokens.
Send a personal access token as a bearer header (Authorization: Bearer <token>), created with
alaf server token create. The dashboard uses your session cookie instead. See the
API overview and the auth model for details.
Endpoints
| Method & path | Permission | What it does |
|---|---|---|
GET /api/tokens | settings:read | List your own tokens (secrets never included). |
POST /api/tokens | settings:write | Mint a token — returns the plaintext secret once. |
DELETE /api/tokens/:id | settings:write | Revoke one of your tokens. |
POST /api/tokens/mcp-authorize | settings:write | Record what an OAuth MCP client may access (consent step). |
GET /api/tokens/mcp-clients | settings:read | List your connected MCP clients. |
DELETE /api/tokens/mcp-clients/:clientId | settings:write | Disconnect an MCP client and revoke its issued tokens. |
The settings:read / settings:write gate means any organization member can manage their own tokens; the
handlers then scope every operation to the calling user, so members never see each other's credentials.
Create a token
Mints a new PAT and returns the plaintext secret in data.token exactly once — it is hashed at rest and
can never be retrieved again. List responses only ever show the short tokenPrefix.
POST /api/tokensProp
Type
Each entry in grants is a TokenGrant:
Prop
Type
curl -X POST https://your-host/api/tokens \
-H "Authorization: Bearer $ALAFSERVER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"deploy-bot","readOnly":false,"grants":[{"resourceType":"project","resourceId":"proj_123","permissions":["read","write"]}]}'# Same thing from the CLI:
alaf server token create "deploy-bot" --grant project:proj_123:read,writeCopy the secret immediately
The response's data.token (an opsh_pat_… string) is shown once and never again. GET /api/tokens only
returns the prefix. If you lose it, revoke the token and mint a new one.
A token can never exceed its minter
Every grant is validated against your own access before the token is created. An unknown resourceType
returns 400 INVALID_RESOURCE_TYPE; granting access you don't hold yourself returns 403 GRANT_EXCEEDS_ACCESS.
The "projects it creates" scope
To hand an agent (e.g. an MCP client) the ability to create new projects and fully manage only the ones
it creates — with zero visibility into anything else — give it a single create grant on the project
collection:
{ "name": "agent", "grants": [{ "resourceType": "project", "resourceId": "*", "permissions": ["create"] }] }create is collection-only: it authorizes POST /api/projects and lets the token list projects
(filtered to its own), but grants no read/write/admin on any existing project. Each project the token
creates auto-adds a { project, <id>, [read, write, admin] } grant, so it fully controls its own work and
nothing else. In the dashboard this is the "Only projects it creates" option on the MCP authorize screen.
List tokens
Returns your tokens with no secret material — each row carries id, name, tokenPrefix, readOnly,
scoped, expiresAt, lastUsedAt, revokedAt, and createdAt.
GET /api/tokensalaf server token listRevoke a token
DELETE /api/tokens/:idalaf server token revoke tok_abc123404 — token not found
The :id isn't one of your own tokens (or was already removed). List your tokens with GET /api/tokens
to get valid IDs.
MCP clients
When an AI assistant connects to Alaf Server over OAuth, the consent page records what that client is allowed to do, then Alaf Server issues it scoped access tokens that run through the same scoped-principal path as a PAT. These routes back that flow and the Settings → Access → Connected clients list.
Authorize a client
Called by the OAuth consent page (browser cookie session) before a token is issued. It persists the user's
chosen read-only flag and resource grants as the client's binding. Grants are validated ⊆ your own access,
identical to token creation. Re-authorizing overwrites the binding's grants wholesale.
POST /api/tokens/mcp-authorizeProp
Type
Consent errors
Missing clientId returns 400 CLIENT_ID_REQUIRED; an organizationId you're not a member of returns
403 ORG_NOT_A_MEMBER. Invalid or over-broad grants fail the same way as token creation
(400 INVALID_RESOURCE_TYPE / 403 GRANT_EXCEEDS_ACCESS).
List connected clients
GET /api/tokens/mcp-clientsReturns one entry per OAuth binding with clientId, name, organizationId, organizationName,
readOnly, scoped, grantCount, authorizedAt, and lastUsedAt.
alaf server api /tokens/mcp-clientsDisconnect a client
Revokes the client's issued tokens first (stopping it immediately), then drops the scope binding, its grants, and the recorded consent so a reconnect re-prompts. Scoped to you — a client shared across users keeps working for everyone else.
DELETE /api/tokens/mcp-clients/:clientIdalaf server api /tokens/mcp-clients/<clientId> -X DELETE