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:
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.
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.customMetadatais 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.
customMetadata
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 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
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.
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
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, or change the scopes of, 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.
Last updated
Was this helpful?