Skip to main content
Every error the API returns has the same shape, sent as Content-Type: application/problem+json:
Some errors add fields: revision_conflict adds current_revision. A problem never contains a provider’s raw response, a stack trace or anything about our internals; those stay in our logs under the same request_id.
Branch on code, never on title or detail. The wording is for humans and changes; the code doesn’t.

Field problems

Validation errors (422 invalid_request) list every bad field, with a JSON Pointer to it, so you can show each message next to its field:

Handling errors in code

When to retry

1

Only retry when retryable is true

That covers 429, 502, 503, 500, and 409 idempotency_in_progress. Everything else needs a change to the request first.
2

Honour Retry-After

When the response has a Retry-After header, wait at least that many seconds.
3

Otherwise back off exponentially, with jitter

For example 1 s, 2 s, 4 s, 8 s, each with up to 50% random jitter, then give up after five tries.
4

Reuse the same Idempotency-Key

A retried create with the same key can never run twice. See Idempotency.

General error codes

These codes can come from any endpoint. Each heading is the anchor that the type link points to.

unauthenticated

401 · not retryable. The Authorization header is missing, isn’t a bearer token, or the key is malformed, unknown, revoked or expired. Every one of these looks the same on purpose. The response carries WWW-Authenticate: Bearer realm="kallabot". Fix: check the key, or create a new one.

insufficient_scope

403 · not retryable. The key is valid but lacks a scope this operation needs. detail names the missing scopes. Fix: use a key with the scope; see scopes.

wrong_auth_plane

403 · not retryable. The operation is never available to API keys, whatever their scopes. Creating and revoking API keys is the main example: only a signed-in person can do it. Fix: do it in the dashboard.

personal_key_required

403 · not retryable. The operation acts as a person, and you sent a workspace key (kb_live_…). Whispering, listening in, taking over, call notes, number and minute requests, and transferring to a typed number all need a personal key. Fix: use a personal key (kbp_live_…).

key_owner_inactive

403 · not retryable. The personal key’s owner has left the workspace or been suspended, so their key no longer works. Fix: use another key.

unsupported_api_version

400 · not retryable. The Kallabot-Version header names a version the API doesn’t know. Fix: send a valid date, or leave the header out. See Versioning.

sdk_version_unsupported

426 · not retryable. This release of a Kallabot SDK is no longer accepted. detail says how to upgrade. Fix: upgrade the SDK. See SDKs.

forbidden

403 · not retryable. A generic refusal not covered by a more specific code. detail says why.

not_found

404 · not retryable. The resource doesn’t exist, or your key isn’t allowed to see it. We never say which: a 403 would confirm that the id exists in someone else’s workspace. Fix: check the id and the key’s workspace.

invalid_request

422 · not retryable. The body, a query parameter or a header isn’t valid. field_problems lists each bad field with a JSON Pointer path. A cursor from a different query also returns this. Fix: correct the fields named.

unsupported_capability

422 · not retryable. The configuration asks for something this release doesn’t do yet (cross-call memory on an agent, for example). It’s a different code from invalid_request because the honest answer is “not yet”, not “you typed it wrong”. field_problems points at each unsupported path.

precondition_required

428 · not retryable. A required header is missing: Idempotency-Key on a create or control action, or If-Match on a change. title names the header. Fix: send it. See Idempotency and Concurrency.

revision_conflict

412 (or 409 where a revision travels in the body) · not retryable as-is. Someone changed the resource after you read it. The body includes current_revision and the response has an ETag. Fix: read it again, reapply your change, send the new revision.

conflict

409 · not retryable as-is. The request clashes with the resource’s current state, such as a name that’s already taken. detail says what. Many endpoints use a more specific code; see endpoint-specific codes.

idempotency_key_reused

409 · not retryable. You reused an Idempotency-Key with a different request body. Fix: use a new key for a new request.

idempotency_in_progress

409 · retryable, Retry-After: 2. An identical request with the same key is still running. Fix: wait and retry with the same key; you’ll get its result.

insufficient_credit

402 · not retryable. The workspace (or the person whose minutes a call would use) has no minutes left. Fix: add minutes, or move some from the pool. See Minutes.

payload_too_large

413 · not retryable. The body is over the limit: 1 MB for almost every endpoint. Fix: send less. An agent config is normally a few kilobytes.

rate_limited

429 · retryable. You went over a rate limit. The response has Retry-After plus the RateLimit-* headers. Fix: wait Retry-After seconds.

capacity_exceeded

429 · retryable, Retry-After: 5. The workspace already has as many calls running at once as it’s allowed. detail gives the numbers. Fix: wait for a call to end, then retry.

provider_unavailable

502 · retryable. A carrier or AI provider behind the request didn’t respond. Fix: retry with backoff.

capability_disabled

503 · retryable. This capability is switched off right now, for example during maintenance. detail names it. Fix: retry later; check with support if it lasts.

internal_error

500 · retryable. Something went wrong on our side. It has been logged. Fix: retry with backoff, and quote the request_id if it keeps happening.

Endpoint-specific codes

Some endpoints return more precise codes inside the general statuses. The most common: Validation field codes you may see in field_problems: emergency_number (calls to emergency numbers are refused), number_not_owned and number_inbound_only (on from), unknown_scope, server_managed_field.