Errors¶
Envelope¶
Developer API errors carry a single detail key:
Match on the HTTP status. Treat detail as a human-readable message whose
wording may change.
detail is not always a string
Request-body schema failures return 422 with detail as an array of
Pydantic error objects rather than a string:
{
"detail": [
{
"type": "missing",
"loc": ["body", "payload", "action"],
"msg": "Field required"
}
]
}
A client that assumes detail is a string will break on these. Check the
status first, or check the type before formatting.
Status codes¶
| HTTP | When |
|---|---|
| 400 | Business validation. Manifest rejected, plan cap reached, invalid deployment transition, unknown action, rollout percentage out of range, channel name not found. |
| 401 | No token, or a token that does not parse or has been revoked. |
| 403 | The endpoint requires a personal access token and you used a project token. Deleting a project is the main case. |
| 404 | The resource does not exist, or belongs to a project your token cannot see. The two are deliberately indistinguishable. |
| 409 | Conflict. Creating a device whose device_id already exists in the project. |
| 422 | The request body failed schema validation. See the array form above. |
| 500 | Unhandled server error. |
Plan limits return 400:
{ "detail": "monthly artifact upload limit reached for current plan; …" }
{ "detail": "Device limit reached for the current plan." }
Device API¶
The device API uses hardware-safe responses
for business outcomes: a device over the plan's device cap gets 200 with
"status": "over_limit", not a 4xx. A fleet must never brick itself over a
billing state.
You only get 4xx/5xx from /check/ or /status/ for genuine misuse
(401/403) or infrastructure failure (5xx).