Skip to content

API keys

This resource manages the keys that authenticate every other call. You can list, create, update, and revoke API keys programmatically, roll a key to a new secret with a grace window so nothing breaks mid-rotation, and read your current rate-limit budget. Creating a key reveals its secret exactly once — there’s no way to read it back later.

For what keys are, the credential families (mfk_live_…, mfk_test_…), and how to mint your first one in the dashboard, see API keys and creating keys. This page is the API for managing them once you’re automating.

Two isolated scopes split read from lifecycle:

  • keys:read — list and get key metadata (never the secret). A compliance reader can introspect keys without mint/revoke power.
  • keys:write — mint, update, revoke, and roll keys.

There is no admin scope. Keys always act as clients, so a key — even one minted by an admin — can never act as platform admin. See the security model.

Method + path Scope Does
GET /v1/api-keys keys:read list your keys (metadata only — never the secret)
POST /v1/api-keys keys:write create a key → reveals the secret once
GET /v1/api-keys/{keyID} keys:read get one key’s metadata
PATCH /v1/api-keys/{keyID} keys:write rename a key and/or narrow its scopes
DELETE /v1/api-keys/{keyID} keys:write revoke a key immediately
POST /v1/api-keys/{keyID}/roll keys:write rotate to a new secret; the old one stays valid 24h
GET /v1/rate-limits any introspect your current rate-limit budget
Name Type Required Description
name string no a human label, e.g. ci-deploy-bot
scopes string[] yes the scopes to grant — write implies read, admin implies write
resource object no pin the key to one resource: { "type": "site", "id": "site_01J7…" }
expires_in_days integer no TTL in days, 1365; default 90
ip_allowlist string[] no exact IPs and/or CIDRs the key may be used from

Worked example — create a key (reveal-once)

Section titled “Worked example — create a key (reveal-once)”

The secret comes back once, in the create response, in the secret field. Store it immediately; subsequent reads of the key return only its metadata.

Create a scoped deploy key
curl -X POST https://api.managed.dev/v1/api-keys \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-deploy-bot",
"scopes": ["sites:read", "deployments:write", "environments:write", "jobs:read"],
"resource": { "type": "site", "id": "site_01J7Q2" },
"expires_in_days": 90
}'
Response (201) — the secret is revealed once
{
"data": {
"id": "key_01JF8R",
"name": "ci-deploy-bot",
"secret": "mfk_live_b1c4d8e2f6a0…", // shown once — store it now, it is hashed at rest
"prefix": "mfk_live",
"last4": "f6a0",
"scopes": ["sites:read", "deployments:write", "environments:write", "jobs:read"],
"resource": { "type": "site", "id": "site_01J7Q2" },
"expires_at": "2026-09-22T01:10:00Z",
"last_used_at": null,
"revoked_at": null,
"created_at": "2026-06-24T01:10:00Z"
},
"request_id": "req_01JAC1"
}

PATCH /v1/api-keys/{keyID} takes a new name and/or a new scopes list. Scopes may only be narrowed: the new list must be a subset of what the key already holds, and a broadening scope is a 403. To widen a key’s reach, mint a new key.

Drop a key's write scopes
curl -X PATCH https://api.managed.dev/v1/api-keys/key_01JF8R \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Content-Type: application/json" \
-d '{ "scopes": ["sites:read", "jobs:read"] }'

POST /v1/api-keys/{keyID}/roll issues a new secret for the same key id while keeping the old secret valid for a fixed 24-hour grace window. Deploy the new secret to your runners, confirm traffic has moved over, and the old one expires on its own — no window where every caller is broken at once.

Roll a key
curl -X POST https://api.managed.dev/v1/api-keys/key_01JF8R/roll \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23"
Response — new secret revealed once, old one in its 24h grace
{
"data": {
"id": "key_01JF8R",
"name": "ci-deploy-bot",
"secret": "mfk_live_9d3a71c0b8f4…", // the new secret — store it now
"prefix": "mfk_live",
"last4": "b8f4",
"scopes": ["sites:read", "deployments:write", "environments:write", "jobs:read"],
"created_at": "2026-06-24T01:10:00Z"
},
"request_id": "req_01JAC9"
}
  1. Roll the key and capture the new secret.
  2. Deploy the new secret to every caller.
  3. Confirm the old secret has gone quiet in the audit log.
  4. Let the 24-hour grace lapse, or DELETE the key to cut both secrets off now.

GET /v1/rate-limits reports your current budget without spending a request against a real endpoint — useful for a client that wants to back off before it’s throttled. Buckets are per key by request class (read, write, create), plus a team ceiling aggregated across your team’s keys. See rate limits for the headers on every response.

Read your rate-limit budget
curl https://api.managed.dev/v1/rate-limits \
-H "Authorization: Bearer mfk_live_9aF2…" \
-H "Forge-Version: 2026-06-23"
Response
{
"data": {
"buckets": [
{ "class": "read", "limit": 1000, "remaining": 991, "reset": 1782192060 },
{ "class": "write", "limit": 100, "remaining": 100, "reset": 1782192060 },
{ "class": "create", "limit": 5, "remaining": 5, "reset": 1782192060 }
],
"team_ceiling": { "limit": 5000, "remaining": 4982, "reset": 1782192060 }
},
"request_id": "req_01JACF"
}