Skip to main content
All Lokutor API errors follow a single structured format:
On WebSockets the same object arrives inside an error frame: {"type": "error", "data": {"code", "message", "detail", "retryable"}}. On /ws/agent and /ws/stt, some runtime errors carry a plain string in data instead ({"type": "error", "data": "AI response service error."}); handle both. Two responses do not use this shape: the synthesis capacity 503 (below) and the Call Center API’s billing errors (below).

Authentication Errors

Errors that indicate the client cannot authenticate. Do not retry these without user intervention. SDK handling:
You can also pass onError to the client’s constructor. It is a constructor option, not a property to assign later.

Validation Errors

Errors that indicate the request was malformed. Fix the request payload and retry.

TTS Errors

Synthesis at capacity

When every synthesis slot is busy, POST /tts/synthesize answers 503 with a Retry-After header (seconds) and a body that is not the error-catalog shape:
Wait Retry-After seconds and send the same request again.

Agent Errors


Billing Errors

Usage is paid from a prepaid balance (Pricing and billing). These errors mean the account cannot pay for what was asked; none of them goes away by retrying. Where they arrive:
  • REST (/tts/synthesize, /stt/transcribe, /denoise): in the error format above.
  • Call Center API: as {"code": "billing.topup_required", "error": "…"}, with the same codes.
  • WebSocket (/ws/agent, /ws/tts, /ws/stt): the refusal comes before the connection opens. The handshake answers 402 with the message as plain text; a browser WebSocket only sees the connection fail (close code 1006), so check the balance when a connection fails at the handshake.
A call already in progress is not cut with an error when the balance runs out: the agent says goodbye and hangs up once the account is $2 below zero, and on the web the socket then closes with 1008 and the reason account balance exhausted. See When the balance runs out.

Internal Errors

Reserved codes

These codes are defined, and appear in the SDKs’ ErrorCode type, but the server does not send them today. Handle them generically (by retryable) if you match on codes exhaustively. The JavaScript SDK’s TTSClient uses internal.timeout for its own client-side timeout (no audio within 30 s, or 15 s without a chunk); that one is never sent by the server.

Client-side codes (JavaScript SDK)

These are raised by @lokutor/sdk itself, not sent by the server.

WebSocket Close Codes

When the connection closes, the WebSocket close code provides additional context:

Retry Strategy

Decide from retryable, and treat 402 (billing.*) as a prompt to top up rather than an error to retry. A complete recipe is in Error handling.

Custom retry logic

If you connect yourself rather than relying on the SDK’s reconnection: