API Keys

Create, list, and revoke API keys programmatically. Use the scope catalog to build least-privilege keys for CI, engines, and automation.

→ API keys — personal and workspace keys, who manages them, offboarding
→ Authentication — how keys are sent on requests
→ Device authorization — CLI sign-in sessions (alternative to long-lived keys)
→ Entitlements — plan limits on API key count


Personal and workspace keys

A key's scope field says who it acts as. It is set at creation and never changes.

scopeActs asuser_idCreated, listed and revoked by
user (default)The person who created itThat personIts creator, and workspace owners and admins
workspaceThe workspace's service accountThe service accountWorkspace owners and admins only

created_by is always the person who created the key. A personal key stops working, and is revoked, when its creator leaves the workspace; a workspace key keeps working. Workspace keys are not available on personal workspaces.

A workspace key never holds the user or apikeys scopes, whatever it was created with, and it cannot sign in, approve devices or OAuth apps, manage the workspace's members, vault or billing, or create keys. See API keys.

All four /apikeys routes also require the create_keys workspace capability, which every member who is a person holds and service accounts do not. A credential without it gets 403 team_role_required even when it carries apikeys:read or apikeys:write. manage_keys (workspace owners and admins) adds workspace keys and every member's keys.


Permission scopes

API keys carry a list of scopes. Every operation the API performs declares the scope it takes (for example reading an agent takes agents:read, storing a secret secrets:write), and the scope is checked on whatever reaches the operation: a REST route, an MCP tool, an agent's tool call, a flow step or a trigger. Each REST page documents the scopes its operations take.

What a credential without the scope gets:

  • A read of a resource that can be public (agents, apps, conversations, files, flows, engines, credentials, knowledge, artifacts): the public rows, as a signed-out caller would. A private row answers 404, a list shows only public rows.
  • A sign-in-only scope (below): 403 requires_sign_in.
  • Any other scope: 403 insufficient_scope. The detail names the scope.

Runs act with the scopes that started them

An agent run, a flow run, a task or a trigger records the scopes of the credential that started or set it up, and everything it does later acts with those scopes: its tool calls, a step that runs after an approval, a cron or webhook fire. A run started with a key that holds only apps:execute cannot read your knowledge or use a secret's value, whoever approves its tool calls. Using a secret's value in a tool takes secrets:reveal. A trigger edited by a narrower key takes that key's scopes.

Hierarchy

  • Resource-level scopes (for example agents, apps) grant every action on that resource (agents:read, agents:write, agents:execute).
  • Action-level scopes grant a single capability (for example flows:execute).
  • * grants full access.
  • Empty scopes on a key means every scope a key can hold (backwards compatibility for keys created before scoped permissions). It never includes a sign-in-only scope.
  • A belt login or web sign-in narrowed when it was approved holds exactly the scopes it was approved with, sign-in-only ones included only when listed.

Keys cannot grant scopes the creator does not hold. If you authenticate with a scoped credential (a limited API key, or a belt login session), POST /apikeys rejects any scope outside your own grant set, and a request with no scopes creates a key with your scopes, not full access. Only an unrestricted creator (a dashboard session, or a key with empty scopes) can create a full-access key.

Common scope strings

GroupScopes
Agentsagents:read, agents:write, agents:execute
Apps / tasksapps:read, apps:write, apps:execute
Knowledge / skillsknowledge:read, knowledge:write
Flowsflows:read, flows:write, flows:execute
Secretssecrets:read, secrets:write, secrets:reveal (a key with secrets:read holds it; an app's OAuth token never does)
Artifactsartifacts:read, artifacts:write
Credentialscredentials:read, credentials:write
Workspacesteams:read, teams:write
API keysapikeys:read, apikeys:write (sign-in only)
Enginesengines:read, engines:write (admin catalog only)
Remotesremotes:read, remotes:write
Billingbilling:read, billing:write (sign-in only)
Settingssettings:read, settings:write
Useruser:read, user:write (sign-in only)
Approvalsapprovals:write (sign-in only)
Adminadmin:read, admin:write (sign-in only)
Storestore:pricing, store:submit (the platform's workspace only)

Sign-in-only scopes

Some scopes are held only by your own sign-in: the web app, or the CLI after belt login. No API key (personal or workspace), OAuth app token or workspace frame session can be granted them, and a key that lists one, or a full-access key, is still refused with 403 requires_sign_in:

  • approvals:write: answering a tool approval, gate, widget or MCP input request, "always allow", and widening what runs without asking (turning on "allow every tool" or skipping hooks on a chat, adding an allow rule or lifting an ask/deny rule on a chat, agent or remote, turning on an agent's permissions.allow_all_tools)
  • user:read, user:write: your profile, sessions, OAuth consents and connected apps, surveys, bounties, feedback
  • apikeys:read, apikeys:write: listing, creating and revoking keys
  • billing:write: subscriptions, checkout, payment methods, vouchers, invoices sent
  • admin:read, admin:write: platform administration. Held only while the sign-in carries platform power: a staff member's own browser session proved by their authenticator app, or a CLI login they elevated. Without it the refusal is admin_session_required, admin_authenticator_required or otp_required, and a caller who is not staff gets 403 forbidden. A sign-in approved with admin:write also holds the store scopes. A staff member who has platform power but whose login was approved without the scope an operation needs gets 403 insufficient_scope naming that scope: run belt login again and approve it, no new elevation needed.

Staff viewing a workspace through an impersonation grant hold none of its sign-in-only scopes except admin.

POST /apikeys refuses a sign-in-only scope with 400. GET /scopes leaves them out of scopes and lists them under account_scopes (with their groups in account_groups), for the device approval page. The admin scopes appear there only for staff.

Store scopes

The store's pricing and submissions are run from the platform's own workspace, not by platform administration:

  • store:pricing: read and change a store version's pricing (draft, evaluate, publish, apply to the listing) and read the sample tasks pricing is checked against.
  • store:submit: submit a resource version to the store on its publisher's behalf.

They are held, when the credential's scopes name them, only while it acts in the platform's workspace as a member: a member's sign-in (no elevation), their API keys, the workspace's own keys, and the agent runs, flows and triggers started there. A staff member with platform power holds them from any workspace. They end when the membership ends, for keys and runs already made too. No other workspace, and no app's OAuth token, ever holds them: naming one on a key elsewhere is refused with 403, and a full-access key carries none. GET /scopes lists them only to callers who hold them.

store:pricing replaces admin:pricing. A key that named admin:pricing no longer holds pricing: create a new one with store:pricing.

Use GET /scopes for the full catalog with labels, descriptions, and preset bundles.


Scope catalog

GET /scopes

Returns every scope a key can hold, UI group, and preset bundle, plus the sign-in-only scopes a login can be approved for (account_scopes, account_groups) and the preset the belt login approval page starts at (login_preset, with the scopes it grants in grants). Public: no credential needed. public_rows on a group says a credential without its read scope still reads the group's public rows. Non-admin callers do not see engine scopes or the engine preset, and only the platform's workspace sees the store scopes.

Example:

bash
1curl https://api.inference.sh/scopes \2  -H "Authorization: Bearer inf_your_key"

Response:

json
1{2  "scopes": [3    {4      "value": "agents:execute",5      "label": "execute",6      "description": "run agents and send messages",7      "group": "agents"8    }9  ],10  "groups": [11    {12      "id": "agents",13      "label": "agents",14      "description": "manage and run AI agents"15    }16  ],17  "presets": [18    {19      "id": "read",20      "label": "read-only",21      "description": "view resources without making changes",22      "summary": [23        "view your apps, agents, and flows",24        "view files, datastores, and projects",25        "view team information"26      ],27      "scopes": [28        "agents:read", "apps:read", "conversations:read", "files:read", "flows:read",29        "datastores:read", "projects:read", "teams:read", "credentials:read",30        "knowledge:read", "artifacts:read", "settings:read"31      ]32    }33  ]34}

Preset bundles

Preset IDLabelTypical use
standardstandardFull developer access. The belt login approval page starts at login_preset: standard plus approvals:write. Every read, execute, and write scope a key can hold, except the engine scopes. Includes billing:read.
run-onlyrun onlyA server that runs apps and agent chats for its users, such as the server proxy: apps:read, apps:execute, agents:read, agents:execute, conversations:read, conversations:write, files:write. It cannot change apps, agents, secrets, keys or billing, or answer approvals.
readread-onlyDashboards and monitoring. Read scopes only, without secrets, API keys, or billing.
engineengineConnect private engines: engines:read, engines:write, apps:execute. Returned to admins only.

Presets are convenience lists — pass individual scope strings when creating a key.

A session or API key stores its scope list when it is created. Scopes added to a preset later are not added to existing credentials. Run belt login again to get a session with the current standard scopes.


List API keys

GET /apikeys or POST /apikeys/list

Requires apikeys:read (sign-in only: an API key cannot list keys) and create_keys. Workspace owners and admins get every key of the workspace; everyone else gets their own personal keys.

Uses cursor pagination (cursor, limit, filters). POST /apikeys/list accepts the same fields in the JSON body.

Listed keys are masked — only the last four characters of the key string are shown (1nfsh-****wxyz).

Example:

bash
1curl -X POST https://api.inference.sh/apikeys/list \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{"limit": 50}'

Response item fields:

FieldTypeDescription
idstringKey ID
namestringDisplay name
keystringMasked key (1nfsh-****wxyz)
scopestringuser (personal key) or workspace
user_idstringWho the key acts as: its creator, or the workspace's service account
created_bystringThe person who created the key
creatorobjectThat person's id, name, full_name, email and avatar_url
scopesstring[]Granted permissions (empty = full access)
last_used_atstringISO timestamp of the last authenticated request. Omitted when the key has never been used. Updates are debounced (about five minutes), so the value can lag behind actual use.
expires_atstringExpiration (optional)
sourcestringOrigin — empty for manual keys; token for temporary keys; device-auth for legacy CLI keys; engine:<id> for cloud-engine credentials; oauth:<client_id> for OAuth-issued keys

Create API key

POST /apikeys

Requires apikeys:write (sign-in only) and create_keys; a workspace key also needs manage_keys. Subject to API key entitlement limits for the workspace.

Request

FieldTypeRequiredDescription
namestringYesDisplay name
scopestringNouser (default) or workspace. A workspace key needs a workspace owner or admin, and fails with 403 otherwise
scopesstring[]NoPermission list. Omit or [] to get the creator's own scopes (full access only if the creator is unrestricted)
expires_atstringNoISO timestamp; must be in the future. 24 hours or less makes it a temporary key

source, user_id and created_by are set by the server; values in the request body are ignored. A workspace key cannot create keys.

A key created by another API key cannot outlive it: if the creating key expires, the new key's expires_at is capped at the creator's, and a missing expires_at is set to it.

bash
1curl -X POST https://api.inference.sh/apikeys \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "name": "ci-deploy",6    "scopes": ["apps:read", "apps:execute", "files:write"]7  }'

A workspace key for CI:

bash
1curl -X POST https://api.inference.sh/apikeys \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{"name": "ci", "scope": "workspace", "scopes": ["apps:execute"]}'

The full key string is returned only in the create response. Store it immediately — list responses never include the plaintext value.

JavaScript SDK:

typescript
1const { data: key } = await client.apiKeys.create({2  name: 'ci-deploy',3  scopes: ['apps:read', 'apps:execute'],4});5console.log(key.key); // only time the full value is available

Temporary keys

A personal key created with expires_at 24 hours or less from now is a temporary key. The server marks it source: "token". A short-lived workspace key is not a temporary key. It works like any other key (same scopes, same workspace pinning) with these differences:

  • It cannot create API keys (no key holds apikeys:write, which is sign-in only), so a leaked one cannot renew itself. POST /apikeys returns 403.
  • It sends no expiry reminder emails.
  • It is listed while live, where it can be revoked, and deleted once expired.
bash
1curl -X POST https://api.inference.sh/apikeys \2  -H "Authorization: Bearer inf_your_key" \3  -H "Content-Type: application/json" \4  -d '{5    "name": "nightly-report",6    "scopes": ["apps:read", "apps:execute"],7    "expires_at": "'"$(date -u -d '+15 minutes' +%Y-%m-%dT%H:%M:%SZ)"'"8  }'

This is what belt auth token does: it mints a 15-minute key for the active workspace so scripts never see the stored login.


Revoke API key

DELETE /apikeys/{id}

Requires apikeys:write (sign-in only) and create_keys. Members revoke their own personal keys; workspace owners and admins revoke any key of the workspace. Anyone else gets 403. Returns the deleted key DTO.

bash
1curl -X DELETE https://api.inference.sh/apikeys/key_abc123 \2  -H "Authorization: Bearer inf_your_key"

Revocation is immediate — in-flight requests may still succeed briefly while caches expire.


Workspace pinning

Keys created in settings → workspace → api keys or via POST /apikeys are pinned to the workspace active when they were created. They ignore X-Team-ID, so a leaked automation key cannot be retargeted at another workspace via a header.

This includes temporary keys from belt auth token: minted from a session that can switch workspaces, the key itself stays on the workspace that was active at mint time.

Device-auth and magic-link session tokens support switching workspaces. See Authentication: workspace context.


Next

we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.