For the complete documentation index, see llms.txt. This page is also available as Markdown.

Manage API Keys

Create, list, update, protect, and revoke workspace API keys programmatically with the Roboflow REST API.

You can manage your workspace's API keys programmatically using the Roboflow API - create new keys, list and inspect existing ones, rename them, attach metadata, disable, protect, and revoke them.

This is the same surface used by the roboflow api-key CLI and the Roboflow MCP server, so an automated agent can provision the key an application needs without a human copy-pasting from the dashboard.

Secrets are write-once. The full key value is returned only when you create (or roll) a key. Every other endpoint returns a non-secret keyId handle plus a short prefix for identification - never the key itself. Store the value securely (e.g. a .gitignore'd .env) at creation time.

Authentication

Send your API key as the api_key query parameter or an Authorization: Bearer <api_key> header, the same as every other REST endpoint (see Authenticate with the REST API). The acting key must belong to the workspace in the path.

These endpoints respect Roboflow's roles and permissions. When the caller is an OAuth token acting for a user, the relevant RBAC actions (create_api_key, update_api_key, revoke_api_key, get_api_key, view_workspace_api_keys) default to workspace owners/admins. A request made with a scoped key (or an OAuth token acting for a user) can only create or grant abilities that the caller itself already has - see Privilege subset rules.

When the caller is a scoped (non-OAuth) private key, it must additionally carry the scope that matches the endpoint:

Endpoint
Required scope

GET list / GET one

api-key:read

POST create

api-key:create

PATCH update

api-key:update

DELETE revoke

api-key:revoke

GET publishable

workspace:read

An unscoped (full-access) private key already satisfies all of these. A key missing the required scope is treated as if the route does not exist - see Errors.

On workspaces with Advanced API Keys, keys created from the dashboard are scoped (not full access) by default: they grant every ability except the API-key management section (api-key:read, api-key:create, api-key:update, api-key:revoke), so a key can't read or manage other keys unless you enable those scopes explicitly. Scopes are stored as explicit leaf scopes: a section umbrella (e.g. model) or a role:<name> preset is expanded to its current leaves at write time, so a key never auto-gains an ability added to that group later. Creating a workspace-wide key (no folderIds) additionally requires the caller to have access to all folders in the workspace.

List API Keys

GET /:workspace/api-keys

Lists the workspace's API keys (masked) and returns the workspace publishable key.

Query

Name
Type
Description
Required

api_key

string

A private API key for the workspace.

includeDisabled

boolean

Include disabled keys in the result (default false).

includeFolders

boolean

Hydrate folder details for folder-scoped keys (default false).

Example Request

Response

Notes:

  • keyId is a stable, non-secret handle used to address a key in the other endpoints.

  • scopes is null for an unscoped (full-access) key, or an array of scope strings for a scoped key.

  • created_on (ISO 8601) and created_by are included only for keys that have those values recorded. Older keys created before this attribution was tracked omit them.

  • created_by is an opaque identifier of who created the key - a user id, api_key:<handle> (when the key was created by another API key), or SYSTEM (created by an automated process). Treat it as a display/audit string; do not parse it.

  • customMetadata is included only when the workspace's plan includes Advanced API Keys. Without that feature the field is absent entirely (even for keys that have metadata).

Get a Single API Key

GET /:workspace/api-keys/:keyId

Returns masked metadata for one key, addressed by its keyId handle.

Example Request

Response

Create an API Key

POST /:workspace/api-keys

Creates a new API key. The secret value is returned once in the key field.

Headers

Name
Value

Content-Type

application/json

Body

Name
Type
Description
Required

name

string

A human-friendly label for the key.

scopes

Array<string> | null

Restrict the key to these scopes. See the three states below. Requires Advanced API Keys.

folderIds

Array<string>

Restrict the key to these project folders. Requires Advanced API Keys.

customMetadata

Map<string, string>

Up to 20 key/value pairs (keys ≤100 chars, values ≤500 chars). Requires Advanced API Keys.

protected

boolean

Create the key in a protected state.

scopes on create:

  • Omitted - the new key inherits the calling credential's own scopes ("create a key like me"). This is plan-independent: a full-access key creates a full-access key; a scoped key creates a key with the same scopes; folders are inherited the same way. A script that omits scopes behaves identically whether or not the workspace has the Advanced API Keys feature.

  • null - an explicit full-access (unscoped) key. The caller must itself have full access (a scoped caller is rejected - see subset rules).

  • [] (empty array) - a valid key with no abilities; every scoped route rejects it. Useful as a placeholder you grant scopes to later.

  • ["model:infer", …] - scoped to exactly those abilities (a section name such as model grants all of that section's scopes).

  • ["role:reviewer", …] - a role preset: expands to that role's scopes at create time. Use a built-in role (labeler, reviewer, owner) or a custom role's name; role:owner means full access. Composable with explicit scopes.

Providing an explicit scopes array ([], a list, or a role: preset), folderIds, or customMetadata requires the Advanced API Keys plan feature (otherwise 403). Omitting scopes (inherit) and null (full) do not - so the default works on every plan.

Example Request

Response

Update an API Key

PATCH /:workspace/api-keys/:keyId

Updates a key's name, scopes, or metadata; protects it; or enables/disables it.

Headers

Name
Value

Content-Type

application/json

Body (send only the fields you want to change)

Name
Type
Description
Required

name

string

New display name.

scopes

Array<string> | null

New scopes (subset of the caller's). See the three states below. Requires Advanced API Keys.

customMetadata

Map<string, string>

Replaces the key's metadata. Requires Advanced API Keys.

protected

true

Protect the key. The API cannot unprotect - see below.

disabled

boolean

Disable (true) or re-enable (false) the key. Requires Advanced API Keys.

The three states of scopes (PATCH semantics differ slightly from create - omitting a field leaves it unchanged):

  • Omitted - the key's existing scopes are left unchanged.

  • null - the key becomes full access (unscoped). The caller must itself hold full access to grant this.

  • [] (empty array) - the key keeps a valid credential but has no abilities.

  • ["model:infer", …] - replaces the key's scopes with exactly this set (a section name expands to all of that section's scopes).

Sending scopes (including [] or null), customMetadata, or disabled requires the Advanced API Keys plan feature.

Example Request

Response

Revoke an API Key

DELETE /:workspace/api-keys/:keyId

Revokes (permanently deactivates) a key. Existing applications using it will immediately fail to authenticate.

Example Request

Response

Protecting a Key

A protected key cannot be disabled or revoked - by the API, the CLI, the MCP server, or the dashboard - until it is unprotected. Use it to stop an automated agent from accidentally taking down a production key.

  • Protect: PATCH with { "protected": true }.

  • Unprotect: can only be done in the dashboard. The API/CLI/MCP intentionally cannot unprotect a key, so a compromised or over-eager agent cannot remove the safety and then revoke the key in one go.

The Publishable Key

Every workspace has a publishable key of the form rf_<workspaceId>. It is:

  • Not a secret - safe to embed in client-side / browser code (e.g. inferencejs).

  • Inference + model-download only - it cannot manage data, train, or manage keys.

  • Permanent - it is derived from the workspace ID, so it cannot be created, rotated, or revoked.

Read it from the publishableKey field on the list/create responses, or directly:

GET /:workspace/api-keys/publishable

Use the publishable key for browser/edge inference and a scoped private key for server-side work. Note that anyone holding the publishable key can run inference against (and download) that workspace's models - that is the intended trade-off of a "publishable" credential.

Privilege Subset Rules

To prevent privilege escalation, a newly created or updated key can never have more abilities than the credential that creates it:

  • When you call these endpoints with a scoped private key, the new key's scopes must be a subset of the calling key's scopes, and its folderIds a subset of the calling key's folders. An unscoped (full-access) key may grant anything.

  • When you call them with an OAuth token acting for a user, the requested scopes are additionally checked against that user's role - you can only grant abilities your role allows.

Requests that exceed what the caller can grant return 403.

Errors

Status
Meaning
Error shape

400

Invalid request body (e.g. an unknown scope, malformed metadata).

{"error": "string"}

403

The caller is authorized for the route but asked to grant abilities beyond what it holds (scopes/folders that exceed the caller's), the plan lacks Advanced API Keys, or an attempt to unprotect via API.

{"error": "string"}

404

Either no key with that keyId exists in the workspace, or the credential lacks the scope the route requires, or it targets a workspace the key doesn't belong to. Roboflow deliberately hides whether the resource exists.

{"error": {"message", "type", "hint"}} for the permission/workspace case; {"error": "string"} for an unknown keyId.

409

The key is protected and cannot be disabled/revoked.

{"error": "string"}

A common gotcha: a request whose credential simply lacks the route's scope returns 404, not 403. A 403 means the call is allowed to manage keys but tried to hand out more than the caller has.

See Errors and Status Codes for the general error format.

Last updated

Was this helpful?