Error model
One envelope for every failure, a stable code to branch on, and a request id to quote when you ask for help.
v1stable
The envelope
Every non-2xx response carries the same shape. message is prose written for a person and may change between releases; code is the stable part.
422
{
"error": {
"code": "validation_failed",
"message": "runtime must be python",
"details": {"field": "runtime"}
}
}404
{
"error": {
"code": "app_not_found",
"message": "app not found"
}
}Branch on the code
Never parse message, and never branch on the status code alone — several distinct refusals share a status.
- 401
- No credential, or one the server does not recognise.
- 403
- Authenticated, but the scope required is not held.
- 404
- The resource does not exist, or is outside the scopes held.
- 413
- The repository source exceeded the accepted size.
- 422
- The request was understood and refused. Read details.
Pagination
List endpoints take limit — between 1 and 200, defaulting to 50 — and return a single named array. Server-side filters are deliberately narrow: app_id on deployments, deployment_id on executions. Anything else belongs in the client, on the page you already fetched.
Unknown values
Render what you were sent
A status, runtime or failure code you do not recognise is a server that is newer than your client. Show the raw value neutrally. Do not coerce it into the nearest state you do know — an unknown status displayed as
succeeded is the worst bug this system can have.