The permission system that governs sessions, Personal Access Tokens, and OAuth clients — and how to pick the right scopes for your integration
5 min readEvery authenticated request carries a permission set. Sessions get the full set of user-facing
permissions; Personal Access Tokens (PATs) and OAuth client tokens get only the scopes you grant
at creation time. A request missing a required scope fails with INSUFFICIENT_PERMISSIONS.
All user-facing scopes, grouped by the resource they govern.
| Category | Scopes |
|---|---|
| User profile | user:read, user:write |
| Projects | projects:read, projects:write |
| Subscription | subscription:read, subscription:write |
| API keys | api-keys:read, api-keys:write, api-keys:delete |
| Organization | organization:read, organization:manage-members, organization:manage-billing, organization:manage-security |
These are the scopes you can include in a PAT's metadata.scopes array or request from an OAuth client. Organization scopes must be requested explicitly and are then capped by the caller's current role in that organization.
When a user signs in, their session carries the full set of user-facing permissions listed above. You cannot narrow a session's scopes — narrowing happens at the UI layer.
When you create a PAT via POST /api/auth/api-key/create, you pass an explicit metadata.scopes array. The token can never exceed the user's own permissions, and any scope you leave out is blocked even if the owning user has it. This is the mechanism you use to lock down a CI bot.
POST /api/auth/api-key/create
{
"name": "Build status poller",
"metadata": { "scopes": ["projects:read", "subscription:read"] },
"expiresIn": 2592000
}OAuth clients request scopes during the Authorization Code flow. The end user approves them at consent time, and the issued access token carries exactly those scopes.
Read the signed-in user's account, login history, email preferences, and data export.
Endpoints:
GET /api/user/meGET /api/user/me/login-historyGET /api/user/me/email-preferencesGET /api/user/me/exportUpdate email preferences and account state (deletion, recovery). Product identity is email-only — there is no profile/handle/avatar mutation.
Endpoints:
PUT /api/user/me/email-preferencesPOST /api/user/me/recoverDELETE /api/user/meList and read projects and Indexhog operational resources inside an organization (tenant route — the organization id is part of the URL path).
Endpoints (representative):
GET /api/user/organizations/{organizationId}/projectsGET /api/user/organizations/{organizationId}/projects/:idGET /api/user/organizations/{organizationId}/projects/:id/verificationGET /api/user/organizations/{organizationId}/projects/:id/urlsGET /api/user/organizations/{organizationId}/projects/:id/issuesGET /api/user/organizations/{organizationId}/projects/:id/statsCreate and update projects, manage verification, inventory, imports, issue status, and engine connections.
Endpoints (representative):
POST /api/user/organizations/{organizationId}/projectsPUT /api/user/organizations/{organizationId}/projects/:idDELETE /api/user/organizations/{organizationId}/projects/:idPATCH /api/user/organizations/{organizationId}/projects/:id/statusPOST /api/user/organizations/{organizationId}/projects/:id/verification/*POST /api/user/organizations/{organizationId}/projects/:id/urlsPOST /api/user/organizations/{organizationId}/projects/:id/importsPATCH /api/user/organizations/{organizationId}/projects/:id/issues/:issueIdRead the current organization's subscription state, plan, and usage counters.
Endpoints:
GET /api/user/organizations/{organizationId}/payments/subscriptionGET /api/user/organizations/{organizationId}/payments/subscription/usageStart checkouts, change subscription details (including seat count), buy one-time add-ons, and open the customer billing portal. Most write endpoints additionally require the requested organization:manage-billing scope and an owner role.
Endpoints:
POST /api/user/organizations/{organizationId}/payments/checkoutPOST /api/user/organizations/{organizationId}/payments/verifyPATCH /api/user/organizations/{organizationId}/payments/subscriptionGET /api/user/organizations/{organizationId}/payments/portalRead organization details, members, security requirements, verified domains, and subscription usage. A delegated credential must request this scope; membership alone does not add it.
Update organization settings and membership: invitations, roles, removals, ownership transfer, and member-facing MFA policy. The effective permission is also capped by the caller's organization role.
Manage billing settings, subscription commands, purchase intents, and verified domains. This scope is owner-only after the role ceiling is applied.
Change organization security configuration such as SSO. This scope is owner-only after the role ceiling is applied.
List OAuth applications and PATs belonging to the signed-in user.
Endpoints:
GET /api/user/oauth-clientsGET /api/user/oauth-clients/:clientIdGET /api/auth/api-key/listGET /api/auth/api-key/getCreate and update OAuth applications and PATs.
Endpoints:
POST /api/auth/oauth2/registerPUT /api/user/oauth-clients/:clientIdPOST /api/user/oauth-clients/:clientId/regenerate-secretPOST /api/user/oauth-clients/:clientId/togglePOST /api/auth/api-key/createPOST /api/auth/api-key/updateDelete OAuth applications and revoke PATs.
Endpoints:
DELETE /api/user/oauth-clients/:clientIdPOST /api/auth/api-key/deleteFor a UI that displays projects and subscription status but never writes.
["user:read", "projects:read", "subscription:read", "organization:read"]For a deploy bot that creates and updates project records and inventory.
["projects:read", "projects:write", "organization:read"]For a service that manages seat counts, credit top-ups, and renewals on behalf of an organization.
["subscription:read", "subscription:write", "organization:read", "organization:manage-billing"]For tooling that provisions and rotates PATs on behalf of a user.
["api-keys:read", "api-keys:write", "api-keys:delete"]PATs may be pinned to a single organization by passing metadata.organizationId at creation time. A pinned token rejects any request whose URL :organizationId does not match the pin — even if the owner is a member of other organizations.
POST /api/auth/api-key/create
{
"name": "Acme prod bot",
"metadata": {
"scopes": ["projects:read", "projects:write", "organization:read"],
"organizationId": "01HZ3K5R4X9Y2V6QF8TJ7W0CDN"
}
}Use this for any token that should only ever touch one tenant. It is a far smaller blast radius than relying on the caller to send the right URL.
When a request lacks a required scope, the API returns:
{
"success": false,
"status": 403,
"code": "INSUFFICIENT_PERMISSIONS",
"message": "Insufficient permissions. Required: projects:write",
"meta": {}
}This is a hard failure — retrying will not fix it. Either issue a new token with the missing scope or sign in as a user who has it.
Scopes cannot be changed on an existing PAT after creation. The POST /api/auth/api-key/update endpoint updates name and enabled status only. To add a scope, revoke
the token and create a new one.
projects:read should never get projects:write, even "just in case."FORBIDDEN responses instead of cross-tenant writes.expiresIn (in seconds) so tokens die on their own. Build rotation into CI secrets management.key value: The tkn_* prefix makes them easy to grep for — do not hand leakers the job.How the API is structured, how authentication works, and how multi-tenant requests are scoped
Sign in, issue a Personal Access Token, and make your first authenticated call
Point any MCP-capable AI client at the hosted MCP server and drive Indexhog over OAuth — no token to paste.
Request headers, response envelope, pagination, and the query conventions shared by every endpoint