API tokens

Create, scope, update and revoke the tokens behind Settings -> API tokens, and approve an OAuth client.

A token is personal: only its owner lists, edits or revokes it, an instance admin included. A bearer request sees only the token it is made with.

A token's effective power is the intersection of what it was granted and what its owner can still do in that team. Two capabilities decide where it acts at all: manage_tokens (the owner's tokens reach the team) and manage_mcp (they may drive it over MCP). Any member may mint a token; minting needs no capability of its own.

The scope is four optional lists on the same input (teamIds, projectIds, folderIds, appIds):

  • Naming a node grants everything under it, now and later.
  • Naming nothing means every team where the owner holds manage_tokens, read live on each request.
  • Naming anything below a team narrows the token inside that team, and the team-wide permissions it holds (members, roles, registries, databases) stop applying there.

Reading

apiTokens

Any signed-in principal. Returns [ApiToken!].

Your own API tokens, whatever team they act in. A bearer request sees only the token it is made with.

query {
  apiTokens {
    id
    name
    prefix
    capabilities
    expiresAt
  }
}

Lifecycle

The raw secret comes back once, in createToken's payload. A token that names no capabilities is view-only.

createToken

Any signed-in principal. Returns CreateTokenPayload.

Create a personal API token with its own permissions. It reaches the teams where you hold manage_tokens and can never do more than you. The raw secret is returned once in the payload.

ArgumentTypeDescription
inputCreateTokenInput!
input.appIds[String!]
input.capabilities[Capability!]
input.expiresAtString
input.folderIds[String!]
input.instanceAdminBoolean
input.mcpAgentStringThe agent this token is for, as the connect wizard names it ("claude-code", "cursor", ...).
input.nameString!
input.projectIds[String!]
input.teamIds[String!]
mutation {
  createToken(input: { name: "ci", capabilities: [deploy_apps, view_logs] }) {
    raw
    token { id prefix expiresAt }
  }
}

updateToken

Any signed-in principal. Returns Boolean.

Change one of your tokens' name, permissions or scope. The secret is unchanged, so tightening a live token costs no rotation. Returns true.

ArgumentTypeDescription
inputUpdateTokenInput!
input.appIds[String!]
input.capabilities[Capability!]
input.expiresAtString
input.folderIds[String!]
input.idString!
input.instanceAdminBoolean
input.nameString!
input.projectIds[String!]
input.teamIds[String!]
mutation {
  updateToken(
    input: { id: "tok_9f1c2ab7d3e4f5a6", name: "ci", capabilities: [deploy_apps] }
  )
}

revokeToken

Any signed-in principal. Returns Boolean.

Delete one of your tokens. The credential stops working immediately in every team it reached, and an OAuth connection's consent and refresh token go with it. Only its owner can. Returns true.

ArgumentTypeDescription
idString!
mutation {
  revokeToken(id: "tok_9f1c2ab7d3e4f5a6")
}

OAuth clients

A web AI client connects through OAuth, and the consent screen mints an ordinary token for it. This is the mutation behind that screen. Revoking the token disconnects the client everywhere it reached.

authorizeMcpClient

Any signed-in principal. Returns Boolean.

Approve an AI client: mint a personal API token with the chosen capabilities and scope, and link it to the client. It cannot grant more than the approver holds. The OAuth handshake itself is then finished by the browser posting to /api/auth/oauth2/consent - that endpoint refuses a server-side call, so it cannot be done here. teamIds names the teams the connection may work in, each gated on manage_mcp there; naming nothing means every team the approver may connect agents to, live.

ArgumentTypeDescription
appIds[String!]
capabilities[String!]
clientIdString!
expectedTeamIdStringThe team the consent screen showed. Not a choice - the server takes the team from the session, but a disagreement is refused rather than connecting the client to a team nobody read.
folderIds[String!]
projectIds[String!]
teamIds[String!]
mutation {
  authorizeMcpClient(clientId: "oauth_client_9f1c2ab7", capabilities: ["view", "deploy_apps"], teamIds: ["team_9f1c2ab7d3e4f5a6"])
}

See also

Did this page help you?

On this page