Skip to content

Commit 30ce465

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/setup
# Conflicts: # skills/data-management/SKILL.md # skills/training-and-evaluation/SKILL.md # skills/training-and-evaluation/active-learning.md
2 parents d0f954d + 6e1533d commit 30ce465

14 files changed

Lines changed: 657 additions & 37 deletions

File tree

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "roboflow",
3-
"version": "0.1.0",
3+
"version": "0.1.1",
44
"description": "Roboflow computer vision skills and MCP tools for datasets, annotation, training, workflows, inference, and deployment.",
55
"author": {
66
"name": "Roboflow",

.codex-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "roboflow",
3-
"version": "0.1.0",
3+
"version": "0.1.1",
44
"description": "Roboflow computer vision skills and MCP tools for datasets, annotation, training, workflows, inference, and deployment.",
55
"author": {
66
"name": "Roboflow",

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ The `npx skills` CLI works with any agent that reads `SKILL.md` files from `.cla
144144
## Available skills
145145

146146
- **api-reference**: REST API and inference API references
147+
- **cloud-storage**: connecting S3/GCS buckets to mirror images into a workspace
147148
- **data-management**: uploading images, labeling, dataset organization
148149
- **inference**: running inference, workflows, workflow templates
149150
- **plans-and-pricing**: Roboflow plans and credit usage

skills/api-reference/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,4 @@ result = model.predict("image.jpg", confidence=40).json()
8989

9090
- `roboflow://skills/api-reference/inference` — inference URL patterns, request/response formats
9191
- `roboflow://skills/api-reference/rest-api` — platform REST API endpoints (CRUD, upload, training)
92+
- `roboflow://skills/api-reference/api-key-management` — creating and managing API keys via REST, MCP tools, and Python CLI. **When creating a key, scope it to least privilege** if the workspace has Advanced API Keys; if not, tell the user scoped keys are an Enterprise feature.
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
# Roboflow API Key Management
2+
3+
> **Source-of-truth note:** This page ships with the Roboflow plugin. If your client has the plugin loaded, prefer the local skill (`roboflow:api-reference`) over fetching `roboflow://skills/api-reference/api-key-management` via `ReadMcpResourceTool` — the MCP resources are a fallback for non-plugin clients and may lag the source repo.
4+
5+
> **Tip:** If you're connected to the [Roboflow MCP server](https://mcp.roboflow.com), prefer its `api_keys_*` tools over raw REST calls — they handle auth and typed responses for you. The REST patterns below stay relevant if you're not using MCP.
6+
7+
Base URLs:
8+
- Production: `https://api.roboflow.com`
9+
- Staging: `https://api.roboflow.one`
10+
11+
**Auth for management endpoints:** pass the API key via `?api_key=` query parameter or `Authorization: Bearer` header. The workspace publishable key (`rf_<workspaceId>`) is NOT accepted — you must use a private API key (or a scoped key that includes `api-key:read` / `api-key:create` / etc.).
12+
13+
Keys are addressed by their non-secret `keyId` field. Secret key values are **never returned** by any list or get endpoint — they are shown exactly once, in the 201 response from the create endpoint.
14+
15+
## Publishable vs. Private Keys
16+
17+
| | Publishable Key | Private API Key |
18+
|---|---|---|
19+
| Format | `rf_<workspaceId>` | opaque secret string |
20+
| Secret? | No — safe to expose in client-side / browser code | Yes — treat like a password |
21+
| Retrieval | `GET /:workspace/api-keys/publishable` | Created via POST; secret shown once |
22+
| Lifecycle | Fixed per workspace; cannot be created or revoked | Can be created, disabled, and revoked |
23+
| Capabilities | Inference and model download on workspace models only | Full API access (scoped by the key's own scopes) |
24+
| Typical use | Browser/edge inference via `roboflow.js` / inferencejs | Server-side automation, CI/CD, MCP tools |
25+
26+
The publishable key grants inference and model-download access on that workspace's models to **anyone who holds it** — treat it accordingly (it's not a secret, but scope it to what you actually publish). Use a scoped private key for any server-side operation.
27+
28+
## REST Endpoints
29+
30+
### List API Keys
31+
32+
```
33+
GET /:workspace/api-keys
34+
```
35+
36+
Query params: `includeDisabled` (bool), `includeFolders` (bool)
37+
38+
Returns all non-secret key metadata for the workspace. The secret value is never included.
39+
40+
```bash
41+
curl "https://api.roboflow.com/my-workspace/api-keys?api_key=KEY"
42+
```
43+
44+
Response:
45+
46+
```json
47+
{
48+
"apiKeys": [
49+
{
50+
"keyId": "abc123",
51+
"name": "CI pipeline",
52+
"prefix": "rf_ci_",
53+
"scopes": ["image:read", "image:create"],
54+
"folderIds": [],
55+
"default": false,
56+
"protected": true,
57+
"disabled": false,
58+
"created_on": "2024-01-15T10:30:00Z",
59+
"created_by": "user@example.com",
60+
"customMetadata": {}
61+
}
62+
],
63+
"publishableKey": "rf_myworkspaceid"
64+
}
65+
```
66+
67+
### Get a Single Key
68+
69+
```
70+
GET /:workspace/api-keys/:keyId
71+
```
72+
73+
```bash
74+
curl "https://api.roboflow.com/my-workspace/api-keys/abc123?api_key=KEY"
75+
```
76+
77+
Response: `{ "apiKey": { ...same fields as above... } }`
78+
79+
### Get Publishable Key
80+
81+
```
82+
GET /:workspace/api-keys/publishable
83+
```
84+
85+
```bash
86+
curl "https://api.roboflow.com/my-workspace/api-keys/publishable?api_key=KEY"
87+
```
88+
89+
Response: `{ "publishableKey": "rf_myworkspaceid" }`
90+
91+
### Create a Key
92+
93+
```
94+
POST /:workspace/api-keys
95+
```
96+
97+
Body fields (all optional):
98+
99+
| Field | Type | Description |
100+
|-------|------|-------------|
101+
| `name` | string | Human-readable label |
102+
| `scopes` | string[] or null | Scope strings to grant (or `role:<name>` presets). Omit to inherit the calling credential's scopes; `null` for full access (if the caller has it); `[]` for no abilities |
103+
| `folderIds` | string[] | Restrict key to specific folders (requires Advanced API Keys) |
104+
| `customMetadata` | object | Arbitrary key-value metadata |
105+
| `protected` | bool | Mark as protected (cannot be disabled/revoked without dashboard action) |
106+
107+
`scopes`, `folderIds`, and `customMetadata` require the **Advanced API Keys** plan feature. A new key can never exceed the caller's own abilities.
108+
109+
**Who can create keys.** The acting credential may create keys only if it is **unscoped** (full access) or was granted the `api-key:create` scope; an OAuth token also needs the `create_api_key` permission. On workspaces with **Advanced API Keys**, new keys created from the dashboard are **scoped** (not full access) and `api-key:create` is **off by default** — include it explicitly only if the key must manage keys.
110+
111+
**Scopes are stored as explicit leaves.** A section name (e.g. `model`) or a `role:<name>` preset is expanded to its current leaf scopes at create time, so a key never auto-gains an ability added to that group later — request the new scope explicitly when you need it.
112+
113+
**Agents: scope to least privilege.** When you create a key on a workspace that has **Advanced API Keys**, you should pass an explicit `scopes` array covering only the abilities the key actually needs (e.g. `["image:create"]` for an upload-only key) rather than a full-access key. If the workspace does **not** have Advanced API Keys, only full-access keys can be created — tell the user that scoped, least-privilege keys are available on the **Enterprise** plan.
114+
115+
**A workspace-wide key** (no `folderIds`) requires the caller to have access to **all** folders in the workspace; otherwise scope the key to specific `folderIds` you can access.
116+
117+
```bash
118+
curl -X POST "https://api.roboflow.com/my-workspace/api-keys?api_key=KEY" \
119+
-H "Content-Type: application/json" \
120+
-d '{"name": "read-only ingest", "scopes": ["image:read"]}'
121+
```
122+
123+
Response (201):
124+
125+
```json
126+
{
127+
"keyId": "abc123",
128+
"key": "rf_ACTUAL_SECRET_VALUE",
129+
"name": "read-only ingest",
130+
"scopes": ["image:read"],
131+
"folderIds": [],
132+
"protected": false,
133+
"publishableKey": "rf_myworkspaceid"
134+
}
135+
```
136+
137+
**The `key` field in this response is the only time the secret is ever exposed.** Store it immediately (e.g. in a secrets manager); it cannot be retrieved again.
138+
139+
### Update a Key
140+
141+
```
142+
PATCH /:workspace/api-keys/:keyId
143+
```
144+
145+
Body fields (all optional — send only the fields you want to change):
146+
147+
| Field | Type | Notes |
148+
|-------|------|-------|
149+
| `name` | string | Rename the key |
150+
| `scopes` | `string[] \| null` | Replace the key's scopes (tri-state — see below). Requires Advanced API Keys |
151+
| `customMetadata` | object | Replace the key's metadata. Requires Advanced API Keys |
152+
| `protected` | bool | Set to `true` only — unprotect is dashboard-only |
153+
| `disabled` | bool | Temporarily disable or re-enable. Requires Advanced API Keys |
154+
155+
**The three states of `scopes`** (PATCH replaces, so omitting the field leaves scopes unchanged):
156+
157+
- **Omitted** - the key's existing scopes are left unchanged.
158+
- **`null`** - the key becomes **full access** (unscoped). Use this to widen a scoped key back to full access; the caller must itself hold full access to grant it.
159+
- **`[]`** (empty array) - the key stays a valid credential but has **no abilities**.
160+
- **`["model:infer", ...]`** - **replaces** the scopes with exactly this set (a section name like `model` expands to all of that section's scopes).
161+
162+
Sending `scopes` (including `[]` or `null`), `customMetadata`, or `disabled` requires the **Advanced API Keys** plan feature.
163+
164+
```bash
165+
curl -X PATCH "https://api.roboflow.com/my-workspace/api-keys/abc123?api_key=KEY" \
166+
-H "Content-Type: application/json" \
167+
-d '{"name": "renamed key", "disabled": true}'
168+
```
169+
170+
Response: `{ "apiKey": { ...updated fields... } }`
171+
172+
Note: `protected: true` can be set via API, but removing the protected flag requires the dashboard. Attempting to disable or revoke a protected key returns 409.
173+
174+
### Revoke a Key
175+
176+
```
177+
DELETE /:workspace/api-keys/:keyId
178+
```
179+
180+
```bash
181+
curl -X DELETE "https://api.roboflow.com/my-workspace/api-keys/abc123?api_key=KEY"
182+
```
183+
184+
Response: `{ "status": "revoked", "keyId": "abc123" }`
185+
186+
Revocation is permanent. If you might need to re-enable the key later, use `PATCH` with `disabled: true` instead.
187+
188+
## OAuth Scopes for Key Management
189+
190+
When using a scoped key or OAuth token to manage other keys, the caller's token needs one or more of these scopes:
191+
192+
| Scope | Grants |
193+
|-------|--------|
194+
| `api-key:read` | List and get key metadata |
195+
| `api-key:create` | Create new keys |
196+
| `api-key:update` | Rename, scope, disable keys |
197+
| `api-key:revoke` | Delete keys permanently |
198+
199+
## MCP Tools
200+
201+
If you're using the Roboflow MCP server, prefer these tools over raw REST calls:
202+
203+
| Tool | Description |
204+
|------|-------------|
205+
| `api_keys_list` | List all key metadata for a workspace |
206+
| `api_keys_get` | Get a single key by `keyId` |
207+
| `api_keys_get_publishable` | Get the workspace publishable key |
208+
| `api_keys_create` | Create a key (returns the one-time secret) |
209+
| `api_keys_update` | Rename, re-scope, or update metadata |
210+
| `api_keys_protect` | Mark a key as protected (no unprotect tool by design) |
211+
| `api_keys_disable` | Temporarily disable a key (reversible) |
212+
| `api_keys_revoke` | Permanently revoke a key |
213+
214+
There is intentionally no `api_keys_unprotect` tool — removing the protected flag requires a deliberate dashboard action to prevent accidental exposure.
215+
216+
## Python CLI
217+
218+
The `roboflow` Python package exposes an `api-key` subcommand:
219+
220+
```bash
221+
# List all keys
222+
roboflow api-key list
223+
224+
# Get a specific key's metadata
225+
roboflow api-key get KEY_ID
226+
227+
# Create a key (capture the secret immediately)
228+
roboflow --json api-key create "my-key-name" | jq -r .key
229+
230+
# Create a scoped key
231+
roboflow --json api-key create "ci-read-only" --scope image:read --scope model:infer | jq -r .key
232+
233+
# Rename a key
234+
roboflow api-key update KEY_ID --name "new name"
235+
236+
# Disable a key (reversible)
237+
roboflow api-key disable KEY_ID
238+
239+
# Mark a key as protected
240+
roboflow api-key protect KEY_ID
241+
242+
# Get the workspace publishable key
243+
roboflow api-key publishable
244+
245+
# Permanently revoke a key
246+
roboflow api-key revoke KEY_ID
247+
```
248+
249+
## Best Practices
250+
251+
### Least-privilege scoping
252+
253+
Only grant the scopes a key actually needs. A key used exclusively to upload images should have `image:create` — not a full unrestricted key. This limits the blast radius if the key is compromised.
254+
255+
```bash
256+
# Good: scoped to what CI actually needs
257+
roboflow --json api-key create "github-actions-upload" \
258+
--scope image:create --scope image:annotate | jq -r .key
259+
```
260+
261+
### Protect production keys
262+
263+
Mark any key used in production workloads as `protected` immediately after creation. Protected keys cannot be accidentally disabled or revoked via API — only via a deliberate dashboard action.
264+
265+
```bash
266+
# Protect a key after creating it
267+
roboflow api-key protect KEY_ID
268+
```
269+
270+
### Never commit secrets
271+
272+
Store the API key in a `.gitignore`'d `.env` file or a secrets manager. The key value is returned only once (at creation time) — if you miss it, you must create a new key.
273+
274+
```bash
275+
# .env (never commit this file)
276+
ROBOFLOW_API_KEY=rf_...
277+
```
278+
279+
### Rotate keys safely
280+
281+
1. Create a new key with the same scopes as the key being rotated.
282+
2. Deploy the new key to all consumers.
283+
3. Verify the new key is working.
284+
4. Revoke the old key.
285+
286+
This zero-downtime rotation avoids service interruptions.
287+
288+
### Prefer disable over revoke when unsure
289+
290+
`PATCH { "disabled": true }` is reversible. `DELETE` is permanent. If you're not certain a key is safe to remove (e.g. you're not sure all consumers have been updated), disable first and revoke after confirming.
291+
292+
### The secret is shown exactly once
293+
294+
The `key` field in the create response is the only time the plaintext secret appears. Copy it to your secrets manager immediately. There is no "show secret again" endpoint — if you lose it, create a new key and revoke the old one.

skills/api-reference/rest-api.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
> **Source-of-truth note:** This page ships with the Roboflow plugin. If your client has the plugin loaded, prefer the local skill (`roboflow:api-reference`) over fetching `roboflow://skills/api-reference/rest-api` via `ReadMcpResourceTool` — the MCP resources are a fallback for non-plugin clients and may lag the source repo.
44
5-
> **Tip:** If you're connected to the [Roboflow MCP server](https://mcp.roboflow.com), prefer its tools (`projects_*`, `versions_*`, `images_*`, `annotations_save`, `models_train`, …) over raw REST calls — they handle auth and typed responses for you. The REST patterns below stay relevant if you're not using MCP.
5+
> **Tip:** If you're connected to the [Roboflow MCP server](https://mcp.roboflow.com), prefer its tools (`projects_*`, `versions_*`, `images_*`, `annotations_save`, `trainings_create`, …) over raw REST calls — they handle auth and typed responses for you. The REST patterns below stay relevant if you're not using MCP.
66
77
Base URL: `https://api.roboflow.com`
88

99
All endpoints require `?api_key=YOUR_KEY` as a query parameter.
1010

11-
API keys are not available programmatically. Users can find theirs at **Workspace Settings > API Keys** in the Roboflow dashboard (`app.roboflow.com/{workspace}/settings/api`).
11+
API keys can be managed programmatically for supported workspaces via the API Key Management endpoints; see [api-key-management.md](api-key-management.md). Users can also find theirs at **Workspace Settings > API Keys** in the Roboflow dashboard (`app.roboflow.com/{workspace}/settings/api`).
1212

1313
## Projects
1414

@@ -168,6 +168,10 @@ pip install roboflow
168168
roboflow import -w my-workspace -p my-project /path/to/images/
169169
```
170170

171+
## API Key Management
172+
173+
See [api-key-management.md](api-key-management.md) for creating and managing API keys via REST, MCP tools, and the Python CLI — including scoped keys, key rotation, and the publishable key.
174+
171175
## Error Responses
172176

173177
| Status | Meaning |

0 commit comments

Comments
 (0)