Skip to the content
Menu: Errors

Errors

A failed request answers with an HTTP status and one body:

JSON
{
  "error": {
    "code": "invalid_request",
    "message": "name must be at most 200 characters.",
    "param": "body.name"
  }
}
FieldAlways thereWhat it is
codeyesStable and machine-readable: branch on this, never on the message.
messageyesPlain English, safe to show to whoever runs the account. It may be reworded.
paramnoThe input at fault: body.name, query.limit, params.id, header.Zentiq-Version.
detailsnoStructured extras for some codes (below).

The request's id is in the X-Request-Id header of every answer, failed or not.

Codes#

HTTPCodeRetry?Meaning
400invalid_requestNo

The request is malformed or a field is invalid; param says which.

401unauthorizedNo

The API key is missing, invalid or revoked.

402insufficient_minutesNo

No minutes left, or the organisation's own monthly cap is reached.

403forbiddenNo

The key lacks a scope (the message names it) or may not take this action.

403upgrade_requiredNo

The plan lacks a feature or a limit is reached; details has feature, plan and limit, and reason: plan_required when a feature's switch is off because the plan doesn't include it.

403feature_not_in_planNo

A session can't start: the plan doesn't include it.

403account_pausedNo

A session can't start: the account is paused.

403candidate_interviewNo

A job interview is never read as a session: a hiring system reads a candidate's interview with GET /candidates/{id}/interview.

403learner_conversationNo

A young learner's conversation stays in the Zentiq dashboard.

404not_foundNo

No such id in this organisation (an id of another kind is not found either).

409conflictNo

The resource's state doesn't allow this; details.reason says why.

409version_conflictNo

If-Match or baseVersion is not the current version; details.currentVersion.

409session_endedNo

The session has ended.

409duo_not_liveNo

The duo is not live yet.

409face_not_readyYes, after a pause

The persona's new or changed face is still being prepared on the live engine (about a minute); retry after Retry-After. details.etaS is the engine's estimate.

409on_holdNo

The record is under a hold and can't change.

409idempotency_conflictNo

The Idempotency-Key was used with a different request.

409idempotency_in_progressYes, after a pause

A request with this Idempotency-Key is still running; retry after Retry-After.

410goneNo

The resource was deleted.

413too_largeNo

The body, a file or a field is too large.

415unsupported_media_typeNo

The file or content type is not supported.

422go_live_refusedNo

The go-live check refused it; details.problems lists what to fix.

429rate_limitedYes, after a pause

Too many requests; retry after Retry-After.

429concurrency_limitYes, after a pause

Too many live sessions at once.

500server_errorNo

Something went wrong on Zentiq's side.

502upstream_errorYes, after a pause

A service Zentiq relies on failed; retry shortly.

503capacityYes, after a pause

No capacity right now; retry shortly.

504upstream_timeoutYes, after a pause

A service Zentiq relies on timed out; retry shortly.

New codes can appear (treat an unknown one by its HTTP status). Each operation in the reference lists the codes it can answer with.

Details worth reading#

Codedetails
upgrade_requiredfeature (what the plan lacks, such as voice.cloning or limits.personas), plan (the cheapest plan that includes it, or null), limit (for a count), and reason: "plan_required" when the feature's switch is off because the plan doesn't include it (where the switch simply off answers 409 conflict). Show it to whoever runs the account.
go_live_refusedproblems: [{ field, message }], what to finish before it can go live; warnings alike, which don't block.
version_conflictcurrentVersion: the version someone else saved first. Read it again, then retry.
conflictreason: why the state doesn't allow it (a duo's persona is changed in the duo studio, a decision was already made, …).

Plan refusals on older keys#

On the 2026-10-01 contract a plan refusal is in the same shape as every other error, its extras in details. A key on the older 2026-07-08 contract gets the shape it always had:

JSON
{ "error": "upgrade_required", "feature": "voice.cloning", "plan": "pro", "message": "Voice cloning is on the Pro plan." }

The SDKs read both.

What to retry#

  • Retry after a pause: rate_limited and concurrency_limit (429, wait for Retry-After), idempotency_in_progress, and the 5xx codes upstream_error, capacity and upstream_timeout.
  • Only if the retry is safe: a GET, PUT, PATCH or DELETE, or a POST sent with an Idempotency-Key to an operation that takes one. Any other POST may have been done already.
  • Never unchanged: every 4xx other than those. Fix the request (or the plan, or the key's scopes) first.

The SDKs do all of this for you: they retry safe requests on the codes above, with a growing pause, twice by default.