Identity and permissions for agents

Understand what your credential can do, why a call was denied, and how to get an agent its own scoped identity. This page is written for an AI agent. It extends the deployment protocol at https://app.tokay.io/llms.txt. Everything here is an escape hatch. The happy path needs none of it, so act on an explicit user request or a named readiness action, not on your own initiative.

Last updated

The authority model

  • There are two kinds of tokens, and they do not mix. Platform credentials (person sessions, API tokens, bot tokens) authenticate against the Tokay API. Machine tokens authenticate against a deployed app's own URL and never work on the API. See the access reference for machine tokens.
  • Effective authority is an intersection. A token acts within the live memberships of its owning principal, and a GRANT_LIMITED token is further narrowed to its explicit grant actions. Holding a credential never adds memberships.
  • Authority is checked on every request. Revoking a token or removing a membership changes what the very next call can do, with no session to expire.
  • Secrets are write only through the API. A credential that can set secret values still cannot read them back, which keeps your context and logs from becoming a credential store.
  • Every material action is attributed to the principal and the specific credential that performed it, so prefer a dedicated agent identity over borrowing a person's token for anything durable.

What to call

To Use
Create an agent identity with memberships and a token createBot
Issue another token for an existing bot createBotToken
See the action implication map permissionActionImplications
Rename or revoke a platform token renameToken / revokeToken
Remove an agent identity removeBot

Create an agent identity

One call creates the bot, its memberships, and its first token:

mutation Bot($workspace: ID!, $project: ID!) {
  createBot(input: {
    workspace: $workspace,
    displayName: "deploy-agent",
    tokenLabel: "deploy-agent-token",
    tokenScopeMode: GRANT_LIMITED,
    grants: [{ action: PROJECT_STATUS }, { action: PROJECT_LOGS }, { action: PROJECT_DEPLOY }],
    projectMemberships: [{ project: $project, role: MEMBER }]
  }) {
    botCreation { token tokenLabel tokenScopeMode expiresAt bot { id } }
  }
}

The token is shown exactly once. tokenScopeMode: FULL_MEMBERSHIP skips grants and gives the token everything the bot's memberships allow. GRANT_LIMITED requires a matching grant for every action, which is the right default for automation. repoMemberships take { repo, role } the same way, expiresAt bounds the token's lifetime, and canMintTokens decides whether the bot can issue further tokens. Use createBotToken(input: { bot, tokenLabel, tokenScopeMode, grants }) to rotate or add tokens later.

Grant actions

Grants use the PermissionActionType enum. The names are the same strings that appear in denial errors.

Action Covers
WORKSPACE_READ Discover and read workspace context
WORKSPACE_CREATE_PROJECT, WORKSPACE_CREATE_REPO Create projects and repositories
WORKSPACE_MANAGE_ACCESS Members, bots, domains, workspace connections
PROJECT_STATUS Read project and service state
PROJECT_LOGS Logs, request payloads, runtime output
PROJECT_QUERY Read only managed resource queries
PROJECT_EXPORT Data exports and service file downloads
PROJECT_WRITE Operate and configure services, trash and restore
PROJECT_DEPLOY Deploy services and enable auto-deploy
PROJECT_FILES_WRITE Service file uploads and rescue copies
PROJECT_INVOKE Run app tasks, scheduled jobs, and test events
PROJECT_MANAGE_RESOURCES Backing resources, outputs, resource restore
PROJECT_WRITE_SECRETS Set and rotate secrets and service overrides
PROJECT_MANAGE_ACCESS Access policy, visitors, machine tokens, endpoints, hosts
PROJECT_DESTROY Purges, storage wipes, snapshot restore, destructive migration confirms, loosening release safety
REPO_READ, REPO_WRITE, REPO_MANAGE_ACCESS, REPO_DESTROY The repository equivalents

Grant the minimum that covers the task. An agent that deploys and verifies typically needs PROJECT_STATUS, PROJECT_LOGS, PROJECT_DEPLOY, and PROJECT_WRITE_SECRETS, and no workspace actions.

Read a denial

Denied calls fail with a precise, parseable message, Not authorized for PROJECT_DEPLOY on SERVICE <uuid>. The named action is the grant to request, and the target tells you whether the gap is a missing grant or a missing membership. Do not retry a denied call unchanged, and do not escalate by asking for FULL_MEMBERSHIP when one named grant would do. Report the exact action and target to the user.

Some actions imply others, so a single grant can carry its prerequisites. The map is queryable:

query { permissionActionImplications(first: 50) { nodes { action impliedAction } } }

Token hygiene

  • Present platform tokens as Authorization: Bearer <token> on every API request.
  • Never write a token or secret value into source, project secrets, or task commands. Attribution already records who did what.
  • revokeToken cuts a platform token on the next request. removeBot retires the identity and everything it can act as.
  • When a user asks you to act beyond your grants, name the exact missing action rather than requesting a broader credential.

Error shapes

You see It means Do this
"Not authorized for ACTION on TYPE id" Missing grant or membership for that target Report the named action and target to the user
A read returns null for an id you were given The database hides rows your credential cannot see Treat it as not yours. Confirm the id and your memberships
A machine token fails on /graphql Machine tokens work only against the app Use a platform credential for the API
A token you just revoked still sits in your context Nothing. Authority is already gone Stop using it. The next call fails