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, unknown model, amount too low
401UNAUTHORIZEDMissing/invalid API key or SIWE signature
402INSUFFICIENT_BALANCENot enough credit for the estimated cost
404NOT_FOUNDResource not found
429RATE_LIMITEDPer-wallet rate limit exceeded
500INTERNAL_ERRORServer error
502UPSTREAM_ERRORLLM provider failed or timed out

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 LLM provider fails on a non-streaming request. The balance lock is released (no charge).

{
  "error": "UPSTREAM_ERROR",
  "message": "Provider returned an error"
}

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