API
Errors and status codes
Failures use the same envelope as successes, with a stable machine-readable code, a human-readable message, and a request ID that identifies the call in our logs.
The error envelope
{
"error": {
"code": "NOT_FOUND",
"message": "Project not found"
},
"meta": {
"requestId": "req_2c91ba4"
}
}| Field | Description |
|---|---|
error.code | Stable identifier. Branch on this — it does not change between releases. |
error.message | Written for humans. Show it, log it, but never match on it. |
error.details | Present on validation failures — see below. |
meta.requestId | Identifies this exact call in our logs. Quote it in support requests. |
Status codes
| Status | Meaning | Retry? |
|---|---|---|
200 / 201 | Success. 201 means a resource was created. | — |
400 | The body or a parameter failed validation. | No — fix the request. |
401 | Missing, malformed, expired or revoked token. | No — issue a new token. |
403 | Valid token without the required scope. | No — grant the scope. |
404 | The resource does not exist, or the token cannot see it. | No. |
409 | Conflicts with current state — a name in use, a resource mid-transition. | Only after re-reading the resource. |
422 | Well-formed request that cannot be carried out, e.g. quota exceeded. | No — change the request or the quota. |
429 | Throttled or temporarily unable to accept the request. | Yes, with bounded exponential backoff and jitter. |
5xx | Something failed on our side. | Yes, with exponential backoff. |
Common error codes
| Code | Status | What happened |
|---|---|---|
BAD_REQUEST | 400 | One or more fields are missing or malformed. |
UNAUTHORIZED | 401 | The bearer token is unusable. |
FORBIDDEN | 403 | The token lacks the scope this endpoint needs. |
NOT_FOUND or PROJECT_NOT_FOUND | 404 | No project with that ID is visible to this token. |
CONTAINER_NOT_FOUND | 404 | No container with that ID exists in the project. |
PROJECT_HIBERNATING | 409 | The project is hibernating and cannot accept this operation yet. |
CONTAINER_NOT_RUNNING | 409 | The requested container action cannot run in its current state. |
Validation errors
A 400 adds a details array naming each field that failed, so you can surface the problem next to the input rather than as one opaque message.
{
"error": {
"code": "BAD_REQUEST",
"message": "Request validation failed",
"details": [
{ "field": "name", "code": "required", "message": "name is required" },
{ "field": "sizeGb", "code": "min", "message": "sizeGb must be at least 1" }
]
},
"meta": { "requestId": "req_2c91ba4" }
}Throttling
Some operations may return 429 when they are throttled. The API does not promise a standard rate-limit header on every response, so use the response status and body as the signal, then apply bounded exponential backoff with jitter. If a future response supplies a server wait hint, prefer that hint.
Retrying safely
Retry 429, 5xx and transport failures such as a dropped connection or a timeout. Do not retry any other 4xx — the request will keep failing until you change it.
Method matters as much as status. GET and DELETE are idempotent and always safe to repeat. A create is not: if it timed out you cannot tell whether the resource exists, so read before you retry.
A timed-out create may still have succeeded
attempt 1 wait 250ms + jitter
attempt 2 wait 500ms + jitter
attempt 3 wait 1000ms + jitter
attempt 4 give up and surface the errorAdd jitter so a fleet of workers does not synchronise its retries into a thundering herd. The SDK pages carry working implementations forTypeScript and Go.
Request IDs
Every response — success or failure — carries meta.requestId, also returned as the X-Request-Id header. Log it alongside your own errors. When you emailsupport@hubfly.space, including it turns a long back-and-forth into a single lookup.