Skip to Content
APIErrors

Error Codes

Reference for API error codes and how to resolve them.

Error Response Format

Errors return a flat JSON envelope:

{ "error": "invalid_input", "detail": "Human readable description of what went wrong", "request_id": "b3c1e2f0-2a4d-4a1e-9c3f-0f2b7c1d5e6a" }
FieldDescription
errorStable, machine-readable code (see below)
detailHuman-readable description of the failure
request_idCorrelation id, also echoed on the x-request-id response header
hintOptional actionable next step (present on some errors)
docsOptional link to relevant documentation (present on some errors)

HTTP Status Codes

StatusDescription
200Success
201Created
400Bad Request - Invalid parameters
401Unauthorized - Missing or invalid authentication
403Forbidden - Insufficient permissions
404Not Found - Resource does not exist
409Conflict - Idempotency-Key reused or request already in progress
429Too Many Requests - Rate limit exceeded
500Internal Server Error

Error Codes

The API uses a short set of stable, lowercase codes in the error field. Some routes add a more specific code (for example WALLET_NOT_CONFIGURED on execution routes); those are documented alongside the endpoint that raises them.

CodeHTTP statusMeaningResolution
unauthorized401Missing or invalid authenticationInclude a valid session cookie or API key
insufficient_scope403The caller is not allowed the required scopeUse a key with the needed scope. For an agent connection, check whether the limit set on the person is holding it below its token’s scope
invalid_input400Request validation failedCheck required fields and formats
not_found404Resource does not exist or is not visible to youVerify the resource id
conflict409Request conflicts with the current stateReconcile and retry
rate_limited429Too many requestsBack off and retry (see rate-limit headers below)
internal_error500Unexpected server errorRetry with backoff; contact support if it persists
mfa_required403The session is flagged and must re-prove its second factorComplete the step-up prompt, then retry

Idempotency Errors

Both codes answer 409 and mean opposite things, so the status does not separate them. These two responses carry retryable, which answers one narrow question: is it safe to send this request again under the same Idempotency-Key?

CoderetryableDescriptionResolution
idempotency_conflictfalseThe Idempotency-Key was reused with a different request body. Response includes originalExecutionId.Rotate to a new key only if this is genuinely different work. If it is the same intent whose body was re-serialized, canonicalize the body and keep the key.
idempotency_in_progresstrueA request with this Idempotency-Key is still being processedRetry shortly under the same key
{ "error": "A request with this Idempotency-Key is already being processed. Retry the same key shortly; do not rotate it.", "code": "idempotency_in_progress", "retryable": true }

retryable: false on a conflict does not mean give up, and it does not mean rotate-and-resend either. It means this body is not the body the key was bound to, and that has two causes wanting opposite responses. If the work is genuinely different, use a new key. If it is the same intent serialized differently — "0.1" against "0.10", network in place of chainId, a reworded memo — then the body drifted, not the intent: canonicalize the body and keep the key. Rotating there escapes the in-flight guard on a request that may already have broadcast, and on these routes that pays twice.

The field appears on these two codes only. Every other status on these routes keeps the semantics documented above, whether or not retryable is present: a 429 is still back-off-and-retry, and a 500 is still worth another attempt under the same key — a 5xx tells you nothing about whether the request was received, so the retry has to be able to match the original.

See Direct Execution for the full idempotency policy.

Workflow run failures

Failures that occur while a workflow run executes, rather than while handling an API request, are surfaced in the run logs with a separate stable code of the form PREFIX-NNNN (for example E-0001 for a run timeout). See Run Error Codes for the full list.

Rate Limiting

A rate_limited error (HTTP 429) means you have exceeded the request budget for the endpoint. Back off and retry using the rate-limit headers below.

Rate-limit headers

Every response from a rate-limited endpoint (both success and 429) carries the current limiter state, so clients can pace requests instead of guessing:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetUnix epoch (seconds) when the window frees a slot
Retry-AfterSeconds to wait before retrying (sent only on 429)

Status and long-poll endpoints additionally return X-Poll-Interval-Hint: the server-recommended number of seconds to wait before polling again. A value of 0 means the resource has reached a terminal state and no further polling is needed.

Anti-abuse endpoints (for example password reset and MFA enrollment) intentionally omit X-RateLimit-Remaining so they don’t disclose a caller’s remaining attempt budget. They still send Retry-After on 429.

Retry Strategy

For transient errors (5xx, rate limits), use exponential backoff:

Wait time = min(base * 2^attempt, max_wait)

Recommended:

  • Base: 1 second
  • Max attempts: 5
  • Max wait: 30 seconds