Error codes
Every API error carries a stable machine code, a broad
type that maps to an HTTP status, a param pointing at the offending field where
relevant, and a doc_url built as
https://docs.managed.dev/errors/<type>/<code> — so an error always points at its
own explanation. Match on code in your client — it’s the part that won’t change.
the error envelope
Section titled “the error envelope”{ "error": { "type": "not_found", "message": "site not found", "code": "site.not_found", "param": "site_id", "doc_url": "https://docs.managed.dev/errors/not_found/site.not_found" }, "request_id": "req_01J9F2KQ"}The doc_url is derived from the type and code — scope.insufficient links to
/errors/permission/scope.insufficient. Always log the request_id; it’s what
support uses to find your request.
error types and statuses
Section titled “error types and statuses”The type is the coarse category and fixes the HTTP status. There are eight:
type |
HTTP | Meaning |
|---|---|---|
invalid_request |
400 |
The request is malformed — a bad field, a missing parameter, an unsupported value. |
authentication |
401 |
The key is missing, malformed, expired, or revoked. |
permission |
403 |
You’re authenticated and can see the resource, but your scope doesn’t allow the action. |
not_found |
404 |
The resource doesn’t exist — or you can’t see it (existence hiding). |
conflict |
409 |
The request conflicts with current state — an idempotency clash, or a runtime that can’t perform the action. |
quota_exceeded |
429 |
A plan quota is exhausted (sites, storage, envs). |
rate_limit |
429 |
You exceeded a rate-limit bucket. Honor Retry-After. |
api_error |
500 |
Something went wrong on our side. Safe to retry idempotent requests. |
machine codes
Section titled “machine codes”code is the precise, stable identifier. Each code below is an anchor, so you can
link a code straight to its explanation.
permission — 403
Section titled “permission — 403”scope.insufficient
Section titled “scope.insufficient”Your key can see the resource but lacks the scope this action
requires. The message names the missing scope. Mint or
down-scope a key that carries it.
not_found — 404
Section titled “not_found — 404”site.not_found
Section titled “site.not_found”No site with that id — or your key’s resource constraint can’t see it (existence hiding; see the 404-vs-403 split).
artifact.not_found
Section titled “artifact.not_found”No build artifact with that id — check GET /v1/sites/{id}/artifacts before rolling
a deployment.
function.not_found
Section titled “function.not_found”No function with that id, or it doesn’t belong to a site you can see.
api_key.not_found
Section titled “api_key.not_found”No API key with that id under your account.
job.not_found
Section titled “job.not_found”No job with that id.
webhook_endpoint.not_found
Section titled “webhook_endpoint.not_found”No webhook endpoint with that id.
webhook_delivery.not_found
Section titled “webhook_delivery.not_found”No delivery with that id under the given webhook endpoint.
resource.not_found
Section titled “resource.not_found”The generic fallback when the missing resource has no dedicated code.
conflict — 409
Section titled “conflict — 409”idempotency.key_reuse
Section titled “idempotency.key_reuse”An Idempotency-Key was reused with a different
request body inside the 24-hour window. Reusing a key with the same body replays the
original response instead.
idempotency.in_progress
Section titled “idempotency.in_progress”The original request with this Idempotency-Key is still in flight. Wait for it to
finish, then retry — the replay will return its response.
capability.unsupported
Section titled “capability.unsupported”The endpoint is real and you’re allowed to call it, but this site’s
runtime can’t perform the action — for
example, executing WP-CLI on a static site. Ask
GET /v1/sites/{id}/capabilities first to avoid it.
invalid_request — 400
Section titled “invalid_request — 400”binding.unsupported
Section titled “binding.unsupported”A function declares a binding kind the platform doesn’t support — for example, cron
bindings are rejected for customer functions.
language.unsupported
Section titled “language.unsupported”A function’s language isn’t one the build toolchain supports.
rate_limit — 429
Section titled “rate_limit — 429”rate_limit.exceeded
Section titled “rate_limit.exceeded”A per-key or team bucket is empty. Wait the Retry-After
seconds before retrying — don’t retry tighter.
the 404-vs-403 split
Section titled “the 404-vs-403 split”The most important distinction in the error model is when you get a 404 instead
of a 403 — it’s deliberate, and it preserves existence hiding:
404for things you can’t see. If your key is constrained to one site and you request another, you getsite.not_found— not “forbidden”. The API never confirms the existence of a resource outside your resource constraint.403for things you can see but can’t act on. If you can see a resource (it’s within your constraint) but your key lacks the scope for the action, you getscope.insufficient— a403. This isn’t a leak, because you could already enumerate the resource.
Ordering guarantees you only ever receive a 403 for something you were already
entitled to know exists. See errors for the full model.