Manage API Keys
Create, list, update, protect, and revoke workspace API keys programmatically with the Roboflow REST API, the roboflow api-key CLI, or the MCP server.
About
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.
HTTP API
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:
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.
A publishable key (rf_<workspaceId>) is not accepted as authentication for these management endpoints. Authenticate with a private key.
List API Keys
GET /:workspace/api-keys
Lists the workspace's API keys (masked) and returns the workspace publishable key.
Query
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:
keyIdis a stable, non-secret handle used to address a key in the other endpoints.scopesisnullfor an unscoped (full-access) key, or an array of scope strings for a scoped key.created_on(ISO 8601) andcreated_byare included only for keys that have those values recorded. Older keys created before this attribution was tracked omit them.created_byis an opaque identifier of who created the key - a user id,api_key:<handle>(when the key was created by another API key), orSYSTEM(created by an automated process). Treat it as a display/audit string; do not parse it.custom_metadatais 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
No key with that keyId exists in the workspace (or it has been revoked), or the credential lacks the api-key:read scope / targets a workspace it doesn't belong to. The permission case returns the object-shaped error {"error": {"message", "type", "hint"}}; an unknown keyId returns {"error": "string"}. See Errors.
Create an API Key
POST /:workspace/api-keys
Creates a new API key. The secret value is returned once in the key field.
Headers
Content-Type
application/json
Body
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.
custom_metadata
Map<string, string>
Up to 20 key/value pairs (keys ≤100 chars, values ≤500 chars). Requires Advanced API Keys.
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
scopesbehaves 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 asmodelgrants 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:ownermeans full access. Composable with explicit scopes.
Providing an explicit scopes array ([], a list, or a role: preset), folderIds, or custom_metadata 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
The caller is allowed to create keys but asked to grant scopes/folders beyond what it holds, or the workspace plan does not include the requested advanced feature. Body: {"error": "string"}.
The credential lacks the api-key:create scope, or targets a workspace it doesn't belong to. Body: {"error": {"message", "type", "hint"}}. See Errors.
The key field is the secret value and is shown only in this response. Save it now; you cannot retrieve it again.
Update an API Key
PATCH /:workspace/api-keys/:keyId
Updates a key's name, scopes, or metadata; protects it; or enables/disables it.
Headers
Content-Type
application/json
Body (send only the fields you want to change)
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.
custom_metadata
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), custom_metadata, or disabled requires the Advanced API Keys plan feature.
Example Request
Response
Returned if you send "protected": false (the API cannot unprotect a key), or if you request scopes the caller cannot grant. Body: {"error": "string"}.
No key with that keyId in the workspace (body: {"error": "string"}), or the credential lacks the api-key:update scope / targets a workspace it doesn't belong to (body: {"error": {"message", "type", "hint"}}). See Errors.
Returned if you try to disable a key that is currently protected.
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:
PATCHwith{ "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
scopesmust be a subset of the calling key's scopes, and itsfolderIdsa 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
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"}
Two error body shapes. Most endpoints return a string error - {"error": "Some message"}. The authentication/permission layer instead returns an object - {"error": {"message": "…", "type": "…", "hint": "…"}} (this is what you get for the permission/wrong-workspace 404 above, and for a missing or invalid key 401). Write consumers that handle both shapes.
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.
CLI
The roboflow api-key command group lets you manage your workspace's API keys from the terminal. It wraps the API key REST endpoints and uses the workspace and credentials from your CLI config (see Install and Set Up the CLI).
The full secret value is shown only when you create a key. Capture it immediately - list/get never reveal it again.
list
List the workspace's API keys.
get
Show details for one key.
create
Create a new key (prints the secret once).
update
Update a key's name, scopes, or metadata.
protect
Mark a key as protected.
disable
Disable or re-enable a key.
revoke
Permanently revoke a key.
publishable
Print the workspace publishable key.
Add --json (a global flag, before the command) to get machine-readable output for scripting, e.g. roboflow --json api-key list.
List keys
Get one key
Keys are addressed by their keyId (the non-secret handle shown in list):
Create a key
The secret is printed once. To capture it in a script, use --json and pipe to jq:
--scope, --folder, and --metadata require the Advanced API Keys plan feature, and you can only grant abilities the credential running the command already has.
Update a key
--scope replaces the key's existing scopes with exactly the set you pass, and --metadata replaces the key's metadata. Send --name on its own to rename without touching either.
Changing scopes or metadata requires the Advanced API Keys plan feature (renaming with --name does not). As with create, you can only grant scopes the credential running the command already holds.
Protect / unprotect a key
A protected key cannot be disabled or revoked via the CLI, API, or MCP. Unprotecting can only be done in the dashboard - there is intentionally no unprotect command, so an automated workflow can't remove the safety and revoke a production key in one step.
Disable / re-enable a key
Disabled keys are rejected by the API but can be re-enabled. A protected key cannot be disabled.
roboflow api-key disable (and re-enabling with --enable) requires the Advanced API Keys plan feature, the same as scoped create with --scope/--folder. This matches the REST API.
Revoke a key
Revoking is permanent. A protected key cannot be revoked from the CLI - unprotect it in the dashboard first.
Get the publishable key
The publishable key (rf_<workspaceId>) is non-secret and safe to embed in browser / inferencejs code. It is inference-only and cannot be created or revoked. See The Publishable Key for details.
Last updated
Was this helpful?