Speko Docs

Errors

The relay's single error envelope and closed error-code set.

The envelope

Every non-2xx HTTP response, every SSE error event, and every WebSocket error frame carries the same JSON envelope:

{
  "error": {
    "code": "insufficient_credit",
    "message": "the organization has insufficient credit for this request",
    "retryable": false,
    "request_id": "rreq_9f2c1a"
  }
}
codestring

One of the closed set below. New codes may be added over time, but codes are never repurposed.

retryableboolean

Whether retrying the same request (same idempotency key) can succeed without you changing anything.

request_idstring

Present when the request was assigned an ID. On request_already_started it names the original request.

Raw provider error bodies are never forwarded; provider failures are normalized into the codes below with fixed messages.

The code set

HTTP statusCodeMeaning
400invalid_requestMalformed body, missing Idempotency-Key, bad routing object, unknown fields
400capability_unsupportedNo routable provider supports what the request needs (for example, tools)
401authentication_failedMissing or invalid bearer token
402insufficient_creditOrganization credit cannot cover the request's ceiling
409request_in_progressSame idempotency key + content, original still admitting — retryable
409request_already_startedSame key + content, original already dispatched; carries the original request_id
409idempotency_conflictSame key, different content
413payload_too_largeBody or audio part over the limits
415unsupported_mediaAudio container could not be parsed
429rate_limitedSlow down
429concurrency_exhaustedOrganization or edge concurrency slots exhausted — retryable
500relay_errorInternal relay failure
502provider_errorThe provider failed after output started
503provider_unavailableNo provider currently available — retryable
budget_exhaustedStream-only: a budget extension was refused mid-stream
lease_expiredStream-only: the session's lease could not be renewed

budget_exhausted and lease_expired have no HTTP status because they can only terminate an already-established stream — as the final WebSocket error frame or SSE error event.

Retry guidance

  • Honor the retryable flag first; it is per-error truth.
  • Retry with the same idempotency key so a request that actually started is not duplicated — you will get request_already_started with the original request ID instead of a second charge.
  • Back off on 429 and 503; they indicate pressure, not a broken request.
  • 4xx codes other than 409/429 mean the request itself must change.

On this page