English
English
Appearance
English
English
Appearance
Complete reference for HTTP status codes the Zorio API returns and how your CRM should handle them.
| HTTP | When | Body |
|---|---|---|
| 200 OK | Most GET / PUT / DELETE successes | { "data": {} } or a paginated list |
| 201 Created | POST creating a new resource | { "data": { "id": ..., "..." } } |
| 202 Accepted | Async action enqueued (e.g. click-to-call, lead import) | { "data": { "call_uuid": "...", "status": "queued" } } |
| HTTP | When | Body |
|---|---|---|
| 302 Found | /api/pbx/cdr/{uuid}/recording redirecting to a signed URL | Empty body, header Location: <signed URL> |
The request is invalid at the HTTP level (malformed JSON, missing required header, ...).
{ "message": "Malformed request body" }Handling: check the JSON is correct and the Content-Type: application/json header is present.
Token missing / invalid / expired.
{ "message": "Unauthenticated." }Handling: redirect the user to login for a fresh token. DO NOT hardcode tokens in the frontend.
Token is valid but missing the required permission.
{ "message": "This action is unauthorized." }Handling: log it and ask an admin to grant the missing permission to the user (e.g. pbx_api_access, telesales_api_access).
The resource doesn't exist in your account's data.
{ "message": "Resource not found" }Security note
When a token is used against the wrong account (e.g. account A asks for data from account B), Zorio also returns 404 rather than 403 so it does not leak existence. Your CRM can't distinguish the two cases.
State-machine violation or unique-constraint conflict.
{ "message": "Extension already exists in this queue." }Handling: refresh local state and notify the user.
Validation error — the body parsed correctly, but one or more field values are invalid.
{
"message": "The given data was invalid.",
"errors": {
"extension_number": ["Extension number format is invalid."],
"outbound_caller_id": ["The selected outbound caller id is invalid."]
}
}Handling:
errors, show each message next to its field in the UI.Rate-limit exceeded.
{ "message": "Too many requests. Retry after 25 seconds." }Response headers:
Retry-After: 25
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1782706011Handling: back off per Retry-After. Implement exponential backoff + jitter to avoid a thundering herd.
Unexpected backend error.
{ "message": "Internal server error" }Handling: retry with exponential backoff (3 attempts, 1s / 4s / 16s). If 500 persists → contact Zorio support with X-Request-Id (if available) or the request timestamp.
Lower-tier failure — most common cases:
{ "message": "PBX did not accept the originate. Please try again." }Handling: retry after a few seconds. If 502 persists → escalate.
Maintenance or temporary overload.
{ "message": "Service temporarily unavailable" }The Retry-After: <seconds> header advises the suggested retry delay.
Upstream timeout. Rare.
Every API call MUST have an error handler. DO NOT assume "200 OK by default".
| Status | Retry? | Reason |
|---|---|---|
| 4xx (except 408, 429) | No | Client-side error — retry won't change the result |
| 408 Request Timeout | Yes (1-3 times) | May be a network blip |
| 429 Too Many Requests | Yes (after Retry-After) | Back off per the header |
| 500, 502, 503, 504 | Yes (exponential backoff) | Temporary server-side errors |
Response header: X-Request-Id: <uuid> — paste it into your support ticket; the Zorio team uses it to trace root cause quickly.
A network error (DNS failure, connection refused, TLS error) is NOT an HTTP status code — there is no body. Catch it separately:
try {
const res = await fetch(url, opts);
if (!res.ok) {
// HTTP error 4xx / 5xx
}
} catch (err) {
// Network error
}