Reference
Errors
Every non-2xx response uses one small JSON envelope. Status codes tell you whose fault it is; message and errors tell you what to fix.
The error envelope#
{
"message": "Human-readable summary of what went wrong.",
"errors": {
"field_name": ["Machine-checkable detail, one string per problem."]
}
}message is always present. errors appears when problems can be attributed to specific request fields (validation) or a specific cause (quota) — its keys are field names or cause keys, each mapping to an array of messages.
{
"message": "The given data was invalid.",
"errors": {
"to": ["The to field must be a valid E.164 phone number."],
"workflow_id": ["The selected workflow has no published version."]
}
}Status codes#
| Code | Meaning | What to do |
|---|---|---|
200 / 201 / 204 | Success / created / deleted | — |
400 | Malformed request — bad JSON, missing body | Fix the request; don't retry as-is. |
401 | Missing, malformed, or revoked API key | Check the Authorization header and key status. |
402 | Plan quota exceeded | Not retryable until the quota resets or the plan changes. See below. |
403 | Key lacks the required scope | Use a key with the right scopes. |
404 | No such resource (or it belongs to another workspace) | Check the ID; Vollo never reveals foreign resources. |
409 | Conflict — e.g. deleting an agent a workflow references, or a trunk with numbers on it | Remove the dependency first; the message names it. |
422 | Validation failed | Fix the fields listed in errors. |
429 | Rate limit exceeded (120 req/min per workspace) | Wait Retry-After seconds and retry. See below. |
500 | Vollo-side error | Safe to retry with backoff; contact support if persistent. |
503 | Temporary unavailability (maintenance, overload) | Retry with backoff; check the status page. |
Rate limiting (429)#
The limit is 120 requests per minute per workspace, across all keys, on a sliding window. Watch X-RateLimit-Remaining on every response and respect Retry-After when throttled:
HTTP/1.1 429 Too Many Requests
Retry-After: 21
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
Content-Type: application/json
{ "message": "Too many requests. Retry after 21 seconds." }A minimal well-behaved retry loop:
async function volloFetch(url, options, attempt = 1) {
const res = await fetch(url, options);
if (res.status === 429 && attempt <= 3) {
const wait = Number(res.headers.get('Retry-After') ?? 5);
await new Promise((r) => setTimeout(r, wait * 1000));
return volloFetch(url, options, attempt + 1);
}
return res;
}Only 429, 500, and 503 are worth automatic retries. Never auto-retry 4xx validation or quota errors.
Quota exceeded (402)#
Actions that consume plan resources — starting calls, minting sessions — return 402 Payment Required when the workspace's quota for the billing period is spent:
{
"message": "Voice minutes quota exceeded for the current billing period.",
"errors": {
"quota": [
"Plan allows 2000 voice minutes per month; 2000 used. Resets 2026-09-01T00:00:00Z."
]
}
}Treat 402 as terminal for the current period: surface it to a human, alert your billing owner, or upgrade the plan. Current consumption is always visible via GET /usage, so you can alarm before hitting the wall.
Telephony-specific failures#
Call setup problems are not HTTP errors — POST /calls returns 201 and the failure appears on the call object and in the call.ended webhook:
status | hangup_reason examples | Meaning |
|---|---|---|
no_answer | ring_timeout | Callee didn't pick up within the ring timeout. |
busy | busy | Destination returned busy. |
failed | trunk_auth_failed, invalid_number, carrier_error | The trunk rejected or couldn't route the call — check trunk credentials and the destination number. |
cancelled | caller_cancelled | The call was ended before it connected. |