Errors
Errors are RFC 9457 problem documents served as
application/problem+json:
{
"type": "https://braesystems.com/docs/errors/server-busy",
"title": "Server busy",
"status": 409,
"detail": "A re-image is in progress. Rebooting now would leave the server without a working OS."
}Branch on type. It is a stable identifier; title and detail are prose and may be reworded.
Catalogue
type suffix | Status | Meaning and what to do |
|---|---|---|
unauthorized | 401 | Missing, malformed, revoked, or unknown API key. Check the Authorization: Bearer bs_live_… header. |
forbidden | 403 | Authenticated, but not permitted. |
not-found | 404 | No such resource in your account. See the note below. |
invalid-request | 400 | The body or parameters failed validation. detail names the field. |
invalid-ssh-key | 400 | The public key could not be parsed. |
duplicate-ssh-key | 409 | A key with that fingerprint is already on the account. |
server-busy | 409 | Another operation is already running on the server. action_id is that operation; poll it and retry once it settles. |
server-not-ready | 409 | The server’s state does not allow the operation. |
unknown-image | 400 | No such image slug. See GET /api/v1/images. |
rate-limited | 429 | More than 1000 requests from one key in a minute. Retry-After says how many seconds to wait. |
internal | 500 | Something broke on our side. Retry; if it persists, contact us with the response. |
Why 404 and not 403
A resource belonging to another account returns 404, exactly as a resource that does not exist
does. This is deliberate: distinguishing the two would confirm that a given id exists somewhere,
letting a caller enumerate other customers’ server and key ids by guessing.
For the same reason, passing an ssh_key_ids value that belongs to another account returns
400 invalid-request rather than 404.
Following a busy server
A server-busy problem carries one extension member, action_id: the operation that is already
running. It is there so a refusal is something you can act on rather than only wait out — poll
GET /api/v1/actions/{id} with it, and retry when status is succeeded or failed.
{
"type": "https://braesystems.com/docs/errors/server-busy",
"title": "Server busy",
"status": 409,
"detail": "A power-on is already in progress.",
"action_id": "077849e2-be4d-474b-b951-4313aff27c30"
}If you do not hold any action id at all, GET /api/v1/servers/{id}/actions lists a server’s recent
actions, newest first.
Rate limits
Each key may make 1000 requests per minute, counted in fixed one-minute windows. Past that, every
request answers 429 rate-limited with a Retry-After header giving the whole seconds until the
window resets — wait that long, not a guessed backoff. Polling an action every ten seconds uses a
fraction of the budget.
Retrying
409 and 429 are worth retrying — the first once the current operation finishes (server-busy
names it), the second after the Retry-After the response carries. 500 is worth one or two retries.
400, 401, 403, and 404 will not change on retry; the request itself needs fixing.
Failed operations are not errors
A reboot or re-image that fails still returns 202 at request time — the request was accepted. The
failure surfaces on the action:
{
"data": {
"id": "6b5a4c3d-2e1f-4a9b-8c7d-6e5f4a3b2c1d",
"kind": "reboot",
"status": "failed",
"error": "Server restarted, but did not hand off to the operating system within 15.0 minutes (last boot state: MemoryInitializationStarted).",
"simulated": false
}
}Always check status when polling, not just the HTTP code.