Skip to content

Create an API key

Minting a key takes a minute and is where you decide everything it can do: its scopes, an optional resource constraint, and how long it lives. Get this right and the key is least-privilege by construction — it can touch exactly what you intended and nothing more.

Before you mint anything, two behaviors decide what your key returns. Lead with these, because they explain responses that otherwise look surprising:

  • Missing scope on a resource you can see → 403 with the code scope.insufficient. The resource exists and you’re entitled to know it exists, but this key wasn’t granted the scope for this action. Add the scope (or use a key that has it).
  • Out of scope on a resource you can’t see → 404 not_found. If a key is pinned to one site, anything outside that pin doesn’t just refuse — it reports as not found. This existence hiding is deliberate: a narrowly-scoped key can’t enumerate what it has no business knowing about. The full rationale is in the security model.

The practical consequence: a 404 from a constrained key often means “outside this key’s scope,” not “deleted.” Widen the constraint, or use a less-restricted key, to confirm.

  1. Open the keys page. In app.managed.dev, go to your account settings and open API keys, then Create key.

  2. Name the key. Use a name that says where it runs — ci-deploy-acme, terraform-prod, grafana-readonly. The name shows up in the audit log and on every last-used record, so future-you can tell which key did what.

  3. Set an expiry (optional, recommended). Pick how long the key lives — up to one year (365 days). Expiry is optional, but a shorter-lived key is safer; rotate before it lapses.

  4. Pick scopes. Search and bulk-select the scopes the key needs — sites:read and jobs:read for a dashboard, add deployments:write for a deploy bot. The isolated scopes — credentials:read, credentials:write, exec:raw, keys:read, keys:write — never ride in via a wildcard and must be added one at a time, on purpose.

  5. Constrain the key (optional). Pin the key to a single site so it can never act outside that boundary — a CI key for one client’s site should be pinned to that site. You can also add an IP allowlist here. Team, project, and environment pin types are defined but not mintable yet Preview.

  6. Confirm and copy the secret. The key is shown once. Copy it into your secret store now — you can’t retrieve it later, only roll or revoke and re-mint.

The Create API key dialog in the app.managed.dev dashboard: a name field, an expiry selector, a searchable scope picker with a checklist of individual scopes and a separately-confirmed “isolated scopes” section, a site-pin dropdown, and an IP-allowlist field. A final confirmation panel reveals the secret once with a copy button and a “you won’t see this again” warning.

When it’s time to rotate — before an expiry, after an offboarding, on any suspicion — roll the key instead of revoking it cold. POST /v1/api-keys/{key_id}/roll (or mf keys roll <id>) mints a new secret on the same key id, and the old secret stays valid for 24 hours. Deploy the new secret everywhere the key runs, and the old one dies on its own. The new secret, like the original, is shown exactly once.

For an immediate kill, DELETE /v1/api-keys/{key_id} (or mf keys revoke <id>) revokes with no grace period.

Start from the scope catalog and grant only what the job needs — read is implied by write, so you never list both. Remember the down-scoping rule: a key can never exceed the role of the principal that minted it, no matter which scopes you check. And when you need to hand a narrow bundle to a laptop or a script quickly, mf login --mint --scopes … is the shipped shortcut — it derives a smaller key from the one you already hold. See scopes for the grammar and the full intersection rule.