invalid_request400
The request was malformed.
Check that the body is valid JSON, every required field is present and each identifier follows the spec §1 grammar. The message names what failed.
Screenery / API errors / unauthorized
unauthorizedHTTP 401Send a credential of the right kind for this route: a publish token to push, a read token to read a private asset, a session for the console. The response never says which check failed; if the credential may have been revoked, issue a new one from the console.
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid credentials.",
"doc": "https://app.screenery.dev/errors/unauthorized",
"request_id": "req_01j…"
}
}
Every error from the Screenery API and the delivery CDN is one JSON body,
the same shape on every route (spec §13):
{ "error": { "code", "message", "doc", "request_id" } }.
code is stable — a client may branch on it forever — and
doc links to that code's page here. Quote
request_id (also sent as the
x-screenery-request-id header) when reporting a problem.
invalid_request400The request was malformed.
Check that the body is valid JSON, every required field is present and each identifier follows the spec §1 grammar. The message names what failed.
unauthorized401Missing or invalid credentials.
Send a credential of the right kind for this route: a publish token to push, a read token to read a private asset, a session for the console. The response never says which check failed; if the credential may have been revoked, issue a new one from the console.
forbidden403This credential may not perform that action.
The credential is valid but its role or kind does not allow this write. Use one with the needed role, or ask an org admin to grant it. Reads of private resources never answer this way; they answer not_found.
not_found404Not found.
Check the path: org, project, asset and ref must all exist and be spelled exactly. A private resource answers this way too when the caller may not see it, so a 404 on a private asset can mean the wrong credential rather than the wrong name.
method_not_allowed405Method not allowed.
The route exists but not for this HTTP method. The Allow header lists the methods it accepts.
conflict409The request conflicts with the current state.
Something already holds this name, the name is tombstoned, or the write would move a channel backwards. Read the current state, then retry with a different name or a newer build.
upload_incomplete409Some declared objects are missing or do not match their hash.
Re-upload only the objects the files list names, then call finalize again. The CLI does this on its own; a hand-written client should retry those uploads, not the whole build.
unsupported_media415Some uploaded objects are not an allowed media type.
The files list says which object and why: not an allowlisted type by its magic bytes, an SVG, or past the dimension cap. Fix the file and push again; no retry changes the bytes.
limit_exceeded429A plan limit was exceeded.
The envelope carries limit and current. Prune old builds or assets, or raise the plan. Retrying without changing anything gets the same answer.
rate_limited429Too many requests.
Wait the number of seconds in the Retry-After header, then retry with backoff. The CLI already does this.
not_implemented501Not implemented in this release.
The route is part of the URL grammar but not of this release. Nothing on the client side fixes it; the route will answer once a release ships it, and the CLI and MCP server only call routes that exist.
internal500Something went wrong.
A fault on our side. Retry once with backoff; if it persists, report the request_id from the envelope (also in the x-screenery-request-id header) so the failure can be found in the logs.