API keys
Preview 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.
Credential families
Section titled “Credential families”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_, mfs_live_,
mfo_at_) 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 | mfs_live_… |
a team (machine principal) | CI/CD, Terraform, deploy bots — survives offboarding |
| OAuth token Roadmap | mfo_at_… / mfo_rt_… |
an integrator app, delegated by a client or team | marketplace integrations, third-party SaaS |
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. OAuth is on the roadmap for delegated, per-user access from third-party apps — design it into your mental model now, but personal and service keys are what ship first.
Live vs test mode
Section titled “Live vs test mode”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.
How keys are stored and protected
Section titled “How keys are stored and protected”A key is shown once, at creation, 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:
- A TTL. Keys expire — the default is 90 days, the maximum is one year. Short-lived keys are a feature, not a nuisance: a leaked key has a deadline. Rotate before expiry.
- 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, so a service token only works from your CI runners or your office egress.
- A mode and a prefix. As above — type, mode, and leak-detection identity, all encoded in the literal string.
Sending a key
Section titled “Sending a key”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.
curl https://api.managed.dev/v1/sites \ -H "Authorization: Bearer mfk_live_9aF2…" \ -H "Forge-Version: 2026-06-23"req, _ := http.NewRequest("GET", "https://api.managed.dev/v1/sites", nil)req.Header.Set("Authorization", "Bearer "+os.Getenv("FORGE_TOKEN"))req.Header.Set("Forge-Version", "2026-06-23")resp, err := http.DefaultClient.Do(req)const res = await fetch("https://api.managed.dev/v1/sites", { headers: { Authorization: `Bearer ${process.env.FORGE_TOKEN}`, "Forge-Version": "2026-06-23", },});export FORGE_TOKEN="mfs_live_…"mf sites listA 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 insufficient_scope. The full mapping is in the errors
reference.