Errors
Preview Errors use the same envelope as success
responses, so you parse them the same way. Every error carries a stable type (which
maps to the HTTP status), a machine-readable code, a human message, and a link to
the exact docs entry — plus the request_id you can quote to support.
The error object
Section titled “The error object”When a request fails, data is replaced by an error object:
{ "error": { "type": "permission", "code": "scope.insufficient", "message": "the api key is missing the required scope: sites:write", "param": "scope", "doc_url": "https://docs.managed.dev/reference/error-codes/#scope.insufficient" }, "request_id": "req_01J9…"}| Field | Type | Description |
|---|---|---|
type |
string | The broad error class. Maps 1:1 to the HTTP status (table below). Branch your handling on this. |
code |
string | A stable, dotted identifier like site.not_found. Use this for precise, machine-driven handling — it never changes for a given condition. |
message |
string | A human-readable explanation. Safe to log; not meant for end-user display verbatim. |
param |
string | The request field that caused the error, when one applies (e.g. site_id, scope). Omitted otherwise. |
doc_url |
string | A deep link to the matching entry in error codes. |
The top-level request_id is always present — see
requests & responses.
Type to HTTP status
Section titled “Type to HTTP status”type |
HTTP | When you’ll see it |
|---|---|---|
invalid_request |
400 |
Malformed body, a bad or missing parameter, a value that fails validation. |
authentication |
401 |
Missing, malformed, expired, or revoked credentials. |
permission |
403 |
You’re authenticated and can see the resource, but your key is missing a required scope, or your role can’t perform the action. |
not_found |
404 |
The resource doesn’t exist — or exists but is outside what your credentials can see (see below). |
conflict |
409 |
The request fights current state: a duplicate, a precondition that doesn’t hold, or a capability this runtime can’t perform (capability.unsupported). |
rate_limit |
429 |
You’ve exceeded a rate limit. Honor Retry-After. |
quota_exceeded |
402 / 429 |
A plan limit is reached (sites, environments, storage). 402 when it’s a billing ceiling, 429 when it’s a throughput ceiling. |
api_error |
500 |
An unexpected error on our side. Safe to retry idempotent requests with backoff; quote the request_id if it persists. |
404 vs 403 — existence hiding
Section titled “404 vs 403 — existence hiding”managed.dev deliberately separates “this doesn’t exist for you” from “you can’t do this.” The rule:
- A resource you can’t see →
404 not_found. If a key’s resource constraint excludes a site, or the site simply belongs to another team, the API answers404— never403. It does not leak the existence of resources you have no business knowing about. This is the same existence-hiding guarantee the dashboard enforces. - A scope you’re missing on a resource you can see →
403 permission. If you can already enumerate a site but your key lacks, say,sites:write, you get403 scope.insufficient. There’s nothing to hide here — you could already list it — so the API tells you plainly which scope you need.
The ordering guarantees you only ever receive a 403 for something you were entitled
to know exists. If you get a surprising 404 where you expected a permissions error,
check your key’s scopes and resource constraint first — the resource is likely outside
your key’s visibility, not just outside its permissions.
Following doc_url
Section titled “Following doc_url”Every error’s doc_url anchors into error codes at the
exact code. The reference lists each code with what triggers it and how to fix it,
so an integration can surface a “learn more” link straight from the payload.