Token Kiosk
API Reference

Error Codes

Error shapes, status codes, and handling behavior.

Most errors follow a consistent shape:

{ "error": "ERROR_CODE", "message": "Human readable description" }

POST /v1/topup is an exception: some of its error responses (invalid payment encoding, mismatched payment, undecodable wallet address, and a 503 when the payment facilitator itself is unreachable) return { "error": "<human-readable message>" } with no separate message field and no machine-readable code. For this endpoint, match on HTTP status rather than parsing error as a code enum.

Error reference

HTTPCodeWhen
400VALIDATION_ERRORMissing/invalid params, amount too low
400UNKNOWN_MODELModel ID is not one this gateway can resolve
401UNAUTHORIZEDMissing/invalid API key or SIWE signature
402INSUFFICIENT_BALANCENot enough credit for the estimated cost
402KEY_BUDGET_EXCEEDEDThe API key's own budget is spent
404NOT_FOUNDResource not found
429RATE_LIMITEDPer-wallet rate limit exceeded
4xxUPSTREAM_CLIENT_ERRORThe model rejected the request — status is the upstream's own
500INTERNAL_ERRORServer error
502UPSTREAM_ERRORThe model failed
504UPSTREAM_DEADLINEThe model did not respond within the deadline

Detailed examples

Insufficient Balance (402)

Returned when balance can't cover the reserved cost estimate.

{
  "error": "INSUFFICIENT_BALANCE",
  "message": "Insufficient balance. Top up at POST /v1/topup"
}

Rate Limited (429)

Returned when per-wallet sliding window limits are exceeded.

{
  "error": "RATE_LIMITED",
  "message": "Rate limit exceeded"
}

Validation Error (400)

{
  "error": "VALIDATION_ERROR",
  "message": "model and messages are required"
}

Upstream Error (502)

Returned when the model fails on a non-streaming request. The balance lock is released (no charge).

{
  "error": "UPSTREAM_ERROR",
  "message": "Upstream LLM provider error"
}

The message is fixed. Diagnostic detail is recorded server-side rather than returned, so the response body never varies with which model served the request.

Upstream Client Error (4xx)

A request the model itself rejects — an unsupported parameter, a prompt beyond its context window — is not flattened to 502. It comes back at the status the model returned, with a fixed message:

{
  "error": "UPSTREAM_CLIENT_ERROR",
  "message": "The upstream model rejected this request"
}

Handling only 502 will miss these. A rejection commonly arrives as 400, and a model's own rate limit arrives as 429 — the same status as this gateway's RATE_LIMITED, but a different code. Match on error, not on status alone.

For stream: true requests, the SSE headers and HTTP 200 status are sent before the upstream call starts, so a mid-stream provider failure cannot become an HTTP 502. Instead it arrives as an SSE event over the already-open 200 connection:

data: {"error":{"message":"...","type":"upstream_error"}}

data: [DONE]

Streaming requests never see the 502 shape above

Check for an error key in each parsed SSE data: payload — don't rely on the HTTP status code or the {error: "UPSTREAM_ERROR", ...} shape for streaming failures. The balance lock is still released in this case.

Error handling behavior

ScenarioBehavior
Insufficient balanceHTTP 402 with top-up instructions and current balance
Provider downRelease lock, return 502, no balance deducted
Invalid modelReturn 400, release lock, no balance deducted
Rate limitedReturn 429, no balance deducted
Invalid API keyReturn 401
Expired/invalid SIWEReturn 401
Top-up below minimumReturn 400 before issuing 402

On this page