Skip to content

API keys

An API key is how a script, CI job, or integration authenticates to the managed.dev public API. Every key carries a set of scopes and, optionally, a resource constraint — together they decide exactly what the key can do, and the key can never do more than the person or team that minted it.

The prefix on a credential tells you its type and its mode at a glance — and, just as importantly, it tells secret scanners. The literal prefixes (mfk_live_, mfk_test_, mfs_live_) are registered with leak-detection partners, so a key pasted into a public repo is matched on the prefix and auto-revoked. See the security model for how leak detection works.

Family Prefix Acts as Use case
Personal key mfk_live_… / mfk_test_… a client (you) scripts, dashboards, the mf CLI, personal automation
Service token Preview mfs_live_… a team (machine principal) CI/CD, Terraform, deploy bots — survives offboarding
OAuth token Preview mfo_at_… / mfo_rt_… an integrator app, delegated by a client or team marketplace integrations, third-party SaaS

A full key looks like mfk_live_<public_id>_<secret> — the public id identifies the key (it’s what you see in listings and the audit log); the trailing secret is the credential itself.

A personal key dies with the client that owns it — revoke the person, and their keys stop working. A service token is bound to a team, so it keeps running when an individual leaves; that’s what you want for a deploy bot or a Terraform state. The API recognizes mfs_live_ tokens today, but their full lifecycle — minting and managing them from the dashboard — is still rolling out. OAuth is on the roadmap for delegated, per-user access from third-party apps — design it into your mental model now, but personal keys are what’s shipped.

The _live_ and _test_ infix selects the mode the key operates in. A _test_ key targets test-mode resources and is the safe default for local development and CI dry runs; a _live_ key acts on your real sites and environments. Keep them separate the way you’d keep production and sandbox credentials apart anywhere else.

A key’s secret is shown once — at creation, or when a roll mints a new one — and never again. managed.dev stores only a SHA-256 hash of the secret — the plaintext is never written to disk, so even a database compromise can’t reveal an existing key. If you lose a key, you don’t recover it; you revoke it and mint a new one.

Every key carries:

  • An optional expiry. Set expires_in_days (1–365) when you mint and the key carries an expires_at; leave it off and the API doesn’t force one. The mf CLI applies a 90-day default when it mints, and that’s the habit worth keeping: a leaked key with a deadline is a much smaller problem. Rotate before expiry.
  • Rolling. POST /v1/api-keys/{key_id}/roll mints a new secret on the same key id. The old secret stays valid for 24 hours, so you can swap the new one into your secret store without a hard cutover.
  • Revocation. Revoke a key instantly from the dashboard or the API. Because keys are opaque and checked against the database on every request, revocation takes effect immediately — there’s no stateless token to wait out. See the security model for why this beats a self-contained JWT.
  • An optional IP allowlist. Constrain a key to a set of source addresses (exact IPs or CIDRs), so a CI key only works from your runners or your office egress.
  • An optional resource pin. Constrain a key to a single site so it can never act outside that boundary. Site pins are what you can mint today; team, project, and environment pin types are defined but deferred Preview.
  • Down-scope-only updates. PATCH /v1/api-keys/{key_id} can rename a key or narrow its scopes to a subset of what it already holds — a broadening scope is refused. To widen a grant, mint a new key.
  • A mode and a prefix. As above — type, mode, and leak-detection identity, all encoded in the literal string.

Pass the key as a bearer token on the Authorization header. That’s the only transport — keys never go in query strings or request bodies.

Authenticate a request
curl https://api.managed.dev/v1/sites \
-H "Authorization: Bearer mfk_live_01JABC…_9aF2…" \
-H "Forge-Version: 2026-06-23"

A request with a missing or malformed key returns 401 authentication; a valid key that lacks the required scope for a resource you can see returns 403 with the code scope.insufficient. The full mapping is in the errors reference.