API reference
Errors
A single error shape, stable machine-readable codes, and what to retry.
Every error returns the same JSON shape, so you can parse one structure everywhere:
{
"error": {
"code": "UPSTREAM_TIMEOUT",
"type": "upstream_timeout",
"message": "The model backend exceeded the allowed processing deadline.",
"request_id": "req_0b903419521e4e2c",
"retryable": true
}
}
Branch on code — it is stable and will not change meaning within /v1.
Treat message as human-readable only; its wording may change.
retryable tells you whether an identical retry could succeed. Use it instead
of guessing from the status code.
Codes
| Code | HTTP | Retryable | Meaning |
|---|---|---|---|
AUTHENTICATION_ERROR |
401 / 403 | No | Missing, invalid, or unauthorised key. |
INVALID_REQUEST |
400 / 422 | No | Malformed or semantically invalid request. |
NOT_FOUND |
404 | No | Unknown endpoint. Only the documented /v1 endpoints exist. |
MODEL_NOT_FOUND |
404 | No | The model does not exist or is not available to your key. |
PAYLOAD_TOO_LARGE |
413 | No | Request body exceeds the maximum size. |
RATE_LIMITED |
429 | Yes | Rate limit exceeded. Honour Retry-After. |
CONCURRENCY_LIMIT |
429 | Yes | Capacity momentarily saturated. Not a quota violation — retry shortly. |
UPSTREAM_TIMEOUT |
504 | Yes | The model exceeded the processing deadline. |
UPSTREAM_ERROR |
502 | Yes | The model backend returned an error. |
UPSTREAM_UNAVAILABLE |
502 | Yes | The model backend could not be reached. |
INTERNAL_ERROR |
500 | Yes | Unexpected server-side failure. |
Request IDs
Every response — success or failure — carries an x-request-id header, and
every error body repeats it in error.request_id. Quote this value in any
support request; it is how we find your exact call in our logs.
You can supply your own instead, and it will be used and echoed back:
curl https://api.civix.com.vn/v1/chat/completions \
-H "Authorization: Bearer $CIVIX_API_KEY" \
-H "X-Request-Id: my-trace-id-123" \
...
Retrying safely
Retry only when retryable is true, use exponential backoff with jitter,
and honour Retry-After when present.
Be careful retrying 504 UPSTREAM_TIMEOUT on a non-streaming request: the
generation that timed out may have consumed real compute. Rather than
retrying the same slow request, prefer switching to "stream": true, which
is not bound by the deadline in the first place.
import time
from openai import OpenAI, APIStatusError
def with_retry(fn, attempts=3):
for i in range(attempts):
try:
return fn()
except APIStatusError as e:
body = e.response.json().get("error", {})
if not body.get("retryable") or i == attempts - 1:
raise
time.sleep((2 ** i) * 0.5)
What errors never contain
Error messages never include stack traces, internal hostnames, ports, or any
other detail about our infrastructure. If you need to understand a failure
beyond what code tells you, send us the request_id.