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_LIMITEDtoken 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.
revokeTokencuts a platform token on the next request.removeBotretires 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 |