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#

JSONShape
{
  "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.

JSON422 Unprocessable Entity — validation
{
  "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#

CodeMeaningWhat to do
200 / 201 / 204Success / created / deleted
400Malformed request — bad JSON, missing bodyFix the request; don't retry as-is.
401Missing, malformed, or revoked API keyCheck the Authorization header and key status.
402Plan quota exceededNot retryable until the quota resets or the plan changes. See below.
403Key lacks the required scopeUse a key with the right scopes.
404No such resource (or it belongs to another workspace)Check the ID; Vollo never reveals foreign resources.
409Conflict — e.g. deleting an agent a workflow references, or a trunk with numbers on itRemove the dependency first; the message names it.
422Validation failedFix the fields listed in errors.
429Rate limit exceeded (120 req/min per workspace)Wait Retry-After seconds and retry. See below.
500Vollo-side errorSafe to retry with backoff; contact support if persistent.
503Temporary 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:

HTTP429 Too Many Requests
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:

JavaScriptRetry on 429
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:

JSON402 Payment Required
{
  "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:

statushangup_reason examplesMeaning
no_answerring_timeoutCallee didn't pick up within the ring timeout.
busybusyDestination returned busy.
failedtrunk_auth_failed, invalid_number, carrier_errorThe trunk rejected or couldn't route the call — check trunk credentials and the destination number.
cancelledcaller_cancelledThe call was ended before it connected.