Errors
https://api.suisse-speech.ch/v1 · X-API-KeyEvery error has the same shape:
{"error":{"code":"tts.unknown_voice","message":"Unknown voice.","request_id":"req_…","detail":null}}
codeis stable and safe to branch on.messageis fixed text, not a description of what went wrong internally.detailappears only for problems in your request that need explaining (chiefly markup) and is capped at 400 characters.request_idis what to quote in a support request.
GET /capabilities returns the full list under errors. The families:
| prefix | when |
|---|---|
tts.* |
synthesis |
stt.* |
recognition |
agent.* |
voice agents (Voice agents); on the WebSocket the code arrives in an error message |
api.* |
the edges of the service, outside any endpoint: an unknown path or method (api.not_found), a body that cannot be read (api.invalid_request), the address-based limiter (api.rate_limited), and the answers given while the service itself is not reachable (api.unavailable, api.timeout, with request_id: null) |
| code | HTTP | retry? |
|---|---|---|
*.invalid_request, tts.text_required, tts.unknown_voice, tts.unknown_language, tts.unsupported_format, tts.invalid_speed, tts.style_too_long, tts.invalid_ssml, stt.invalid_audio, agent.invalid_audio, agent.duration_limit |
400 | no: fix the request (agent.duration_limit: open a new session) |
*.unauthenticated |
401 | no |
*.forbidden, agent.not_enabled |
403 | no (agent.not_enabled: voice agents are released per account, contact us and we enable them) |
api.not_found |
404 | no: unknown path, or a method the endpoint does not take |
tts.text_too_long, stt.audio_too_large |
413 | no |
stt.unsupported_format |
415 | no |
*.rate_limited |
429 | yes, after Retry-After: a burst, or (with a detail saying so) the daily audio budget, which starts afresh at 00:00 UTC |
*.entitlement_exhausted |
429 | no: the free minutes or the credit are used up |
agent.usage_limit |
429 | no: the session reached its usage limit |
*.capacity |
429 | yes, after Retry-After |
*.plan_limit_reached |
429 | not by itself: reduce parallelism or raise the limit |
tts.unavailable, stt.upstream_unavailable, agent.unavailable, api.unavailable |
503 | yes, with backoff |
*.timeout |
504 | yes (agent.timeout: no audio and no message for 120 s; open a new session) |
*.internal_error, tts.encode_failed |
500 | once, then contact us |
Voice agents answer in two ways. Before the socket is open (the handshake and
POST /agent/tokens), the HTTP status is the answer: 401, 429 with
Retry-After, 503; the token endpoint also returns the JSON body above. Once
the socket is open, the same codes arrive in an error message, and the HTTP
status in the table is only their family; what tells you whether the session
goes on is the close code, 1000, 1001, 1008, 1009, 1011 or 4000 to 4003
(Voice agents). The
WebSocket error carries code, message, detail and request_id at the
top level, not inside an error object.
Four different things answer with 429, and only error.code tells them
apart. An HTTP client that reports only the status line hides this distinction,
so log the response body of every 429.
Failures are deliberately uninformative about their cause. A credential
problem on our side and a fault on our side both surface as unavailable. This
is intentional: the error surface does not describe our internals.
Recommended client behaviour: retry 503/504 with exponential backoff and
jitter, honour Retry-After exactly on 429, and never retry a 4xx other
than those two without changing the request.
What will not change without notice
- Voice identifiers and what they sound like.
- Error
codevalues. - The realtime frame vocabulary.
- The meaning of the metering unit.
Things that will change and should be read at runtime: the voice list, the
language list, formats, limits and capacity figures. GET /capabilities is the
contract for those.