API reference
https://api.suisse-speech.ch/v1 · X-API-KeyGenerated from the OpenAPI 3.0.3 description of Suisse Speech API 1.9.0. Download it as openapi.json to generate a client or import it into your API tool.
About this API
Speech-to-text and text-to-speech for Swiss German, its regional dialects and other languages, 60 in all. Both directions are available in two modes:
- batch — send a whole file or a whole text, get the whole result
- realtime — stream over a WebSocket and receive results as they are produced
Since 1.9.0 there is a third product on top of the two: voice agents
(GET /agent/stream), realtime speech-to-speech over one WebSocket. The
caller's audio goes in, the agent's spoken answer comes out, following the
instructions and tools you give it. Billed per second of connected session time.
Start with GET /capabilities: it lists every voice, language, dialect,
audio format, parameter range and error code this deployment accepts, so a
client never has to hard-code them.
All endpoints require an X-API-Key header with a valid sv_* API key.
The environment travels with the key: sv_… is live, sv_test_… is the
sandbox — not billed, no allowance consumed, its own budget and concurrency
slice. A sandbox key always receives structurally identical synthetic
audio and transcripts without any engine being called: free, instant and
deterministic, for continuous integration. Real voices and recognition need
a live key. The header X-Suisse-Sandbox-Mode: synthetic is still accepted
but no longer needed. Every
response reports which environment answered in X-Suisse-Environment.
WebSocket endpoints additionally accept
Sec-WebSocket-Protocol: suisse.v1, key.<api key> for browser clients.
Limits apply per API key: 600 requests/minute with a burst limit of 25
requests/second, a ceiling on simultaneous requests, and a daily budget of
audio seconds. An evaluation key additionally carries a fixed allowance of audio
hours. See limits and usage_today in GET /capabilities, and the remaining
allowance in GET /usage. Synthesis and recognition are both metered in audio
seconds, to the second, with no minimum per request.
Reading a 429. Four different things answer with status 429, and error.code
in the response body says which one it is:
*.rate_limited- too many requests in the window, or the daily audio budget is spent. Retry afterRetry-After.*.capacity- the service is momentarily busy. Retry afterRetry-After.*.plan_limit_reached- too many simultaneous requests on this key. Reduce parallelism; retrying at the same parallelism does not help.*.entitlement_exhausted- the evaluation allowance of audio hours is used up. Retrying does not help; contact us to extend it.
An HTTP client that reports only the status line hides this distinction, so log the response body of every 429.
Synthesised audio and transcripts are processed in memory and not stored;
identical texts are synthesised again each time. Every synthesis response
reports cache as miss (the field stays for compatibility).
Keeping audio of fixed prompts (opt-in per key). On the customer's written
instruction we enable, for a named API key, that synthesised audio is kept
when a request asks for it (cache: use | refresh | off,
cache_ttl_s): encrypted, for that key alone, for at most 30 days, deletable
at any time (DELETE /tts/cache). Only such a key sees the cache states
off, hit, partial, miss, refresh and the fields cache_entry and
cache_expires_at. Without the cache switched on for your key these fields
are ignored and responses report "miss".
Simultaneous work is admitted up to a published ceiling and refused beyond it
with 429 and a Retry-After header, rather than queued — a queue would
turn a capacity problem into a latency problem for the requests that did get
in. error.detail names which ceiling was reached: key_capacity (reduce
your own parallelism), service_capacity or host_load (back off and
retry). The current ceilings are in capacity in GET /capabilities, and
are what the deployment enforces today rather than a contractual
entitlement.
A narrative integration guide, including worked examples and client
behaviour for each error class, ships with the service as API_GUIDE.md.
/ttsSynthesise speech from text (batch)https://api.suisse-speech.ch/v1/tts
Send the complete text, receive the complete audio.
By default the response body is the audio itself, with the media type of
the requested format and the metadata in X-Suisse-* headers. Set
output: "json" (or send Accept: application/json) to receive a JSON
envelope with base64 audio instead.
Models
The API provides two specialized speech engines:
studio(default) — Our premier expressive engine. Natural prosody, nuanced delivery, and native support for regional Swiss German dialect translation and nuances. Recommended for production announcements, IVR greetings, audio content, and high-fidelity speech.swift— Low-latency engine optimized for real-time conversational streaming, telephone interactions, and instant responses.
One voice in both modes (since 1.7.0, enabled per account): swift and studio
speak with the same voices, both stream on /tts/stream and both render in batch.
Every passage then starts 60 ms before its first sound and ends 200 ms after its
last, every voice and language is delivered at one loudness, and a sentence sent
whole is rendered in one piece. GET /capabilities shows the modes per model for
your key.
Swiss German Dialects
language selects the language, dialect the regional variety within it. Swiss German (de-CH) is dialect-aware:
standard: supra-regional Swiss German (default).zurich: Zurich dialect (Züritüütsch).bern: Bernese dialect (Bärndütsch).basel: Basel dialect (Baaseldütsch).luzern: Lucerne dialect (Lozärnerdütsch). Both models render the text into the dialect - its own words and forms, not an accent - so the text may be written in Standard German.swiftdoes it in real time on the stream;studiodoes it with the highest fidelity and is the one to use for prepared audio. The compact formlanguage: "de-CH-zurich"is accepted as an alternative to the two fields.
Regional varieties and accents
The catalogue has two levels. language is a code that may carry a region, and a
regional standard variety is its own code: it-CH, fr-CA, en-GB, en-IN,
de-AT. dialect is a variety inside one language: de-CH + stgallen,
fr-CH + geneve, en-GB + scottish. The compact form <language>-<dialect>
(de-CH-stgallen) is accepted wherever a language code is, by recognition too.
Switzerland: Swiss German in its regional dialects and as Swiss High German
(de-CH + hochdeutsch, spoken word for word), Swiss French with the accents of
the Romandie, and Swiss Italian (it-CH). Swiss German dialects are rendered into
the dialect; every other variety keeps the words exactly as written and changes
only pronunciation, melody and rhythm.
GET /capabilities is the list. Every language and dialect there carries a
status: verified is measured in production, supported is rendered on a
best-effort basis and no quality figure is published for it.
Following the language of a conversation
Recognition reports the language it detected in lang, in the same codes this
endpoint accepts as language, the compact dialect form included. A client that
lists its target locales as recognition candidates (lang = de-CH-zurich,fr-CH,en)
receives exactly one of them back and can pass it here unchanged. No mapping table
and no server-side session is involved: every request stands on its own. The reply
text may be written in Standard German; for de-CH it is spoken in the chosen dialect.
For High German use de-CH-hochdeutsch (Swiss High German) or de.
Custom Style Prompting
style is a free-text delivery prompt (up to 600 characters) — e.g. "freundlich und herzlich", "ruhig und geduldig, kurze Pausen nach jedem Satz", or "professionell und sachlich". It steers how the text is spoken, never what is spoken: the supplied text is always rendered verbatim.
Markup
A text value whose first element is <speak> is parsed as markup.
Each construct is realised for real rather than passed on as a hint:
| element | effect |
|---|---|
<break time="500ms"/> |
silence of exactly that length in the audio |
<say-as interpret-as="digits|telephone|characters|ordinal|date|time|currency|number"> |
rewrites the text into its spoken form |
<sub alias="Aktiengesellschaft">AG</sub> |
speaks the alias |
<prosody rate pitch volume> |
real rate, pitch and gain changes on that passage |
<emphasis level="strong"> |
delivery directive on that passage |
<lang xml:lang="de-CH-bern"> |
switches language and dialect mid-text |
<mark name="x"/> |
returns the offset of that point in marks |
<p> <s> |
paragraph and sentence pauses |
say-as with digits or telephone speaks Swiss numbers the way the
our phone agents do: dialect digit words and 3-3-2-2 grouping,
so +41 31 533 22 02 in Bernese becomes
"null drüü eis, füf drüü drüü, zwöi zwöi, null zwöi".
Elements that are not supported are rejected by name with an
explanation in error.detail, never silently ignored. <audio> and
<phoneme> are refused deliberately; use <sub alias> to respell a word.
Call GET /capabilities for the authoritative list.
Metering
Billed in produced audio seconds, measured to the second, with no minimum per request.
Request body · application/json · SynthesisRequest
| Field | Type | Description |
|---|---|---|
model | "studio" | "swift" | Speech synthesis model:
default "studio" |
textrequired | string | The text to speak. Rendered verbatim; never interpreted as an instruction. max 8000 chars |
voice | string | Voice id from default "anna" |
language | string | Language code. default "de-CH" |
dialect | string | Variety within the language.
default "standard" |
speed | number | Speaking rate multiplier (0.5 = half speed, 1.0 = normal pace, 2.0 = double speed). swift applies it exactly. studio has the voice perform it, so the pace changes audibly but not by the exact factor (measured: 1.5 about 1.25 times faster, 0.7 about 0.8 times as fast). default 1 · min 0.5 · max 2 |
style | string | Free-text delivery prompt (up to 600 chars) steering tone, emotion, and pacing. Examples:
max 600 chars |
format | "wav" | "mp3" | "opus" | "pcm16" | "mulaw" | "alaw" | mp3 is constant bitrate: 96 kbit/s at 44.1 and 48 kHz, 80 kbit/s at 32 kHz. default "mp3" |
sample_rate_hz | integer | Output sample rate. Permitted values depend on |
output | "audio" | "json" |
default "audio" |
cache | "off" | "use" | "refresh" | The key's own cache, for keys for which keeping audio was enabled on written instruction
( |
cache_ttl_s | integer | How long a new rendering is kept, in seconds: from 60 to the key's min 60 · max 2592000 |
Responses
| Status | Description |
|---|---|
200 | Synthesised audio. Body is the raw audio unless Schema: file |
400 | Invalid parameters (unknown voice, language, dialect, format or speed) Schema: Error |
401 | Authentication required Schema: Error |
413 | Text exceeds the length limit Schema: Error |
429 | Rate limit, daily audio-seconds budget, or concurrency ceiling.
Schema: Error |
503 | Service temporarily unavailable Schema: Error |
504 | Request timed out Schema: Error |
/tts/streamSynthesise speech from text (realtime, WebSocket)wss://api.suisse-speech.ch/v1/tts/stream
Open a socket, push text as it becomes available, receive audio while the rest of the text is still being written. Lower time-to-first-audio than batch, and the right mode when the text comes from a live source.
Authenticate with the X-API-Key request header, or — from a browser,
which cannot set handshake headers — with the subprotocol field:
Sec-WebSocket-Protocol: suisse.v1, key.<api key>.
Client to server
{"type":"session.start","model":"swift","voice":"anna","language":"de-CH","dialect":"zurich",
"speed":1.0,"style":"freundlich","format":"pcm16","sample_rate_hz":24000}
{"type":"text.append","text":"Grüezi. "}
{"type":"text.flush"}
{"type":"session.end"}
Add "transport":"binary" to session.start to receive audio as raw
binary WebSocket frames instead of base64 inside JSON — a third fewer
bytes and no decode step. Control frames stay JSON, so a binary frame is
always audio, in order, in the encoding session.ready names. The
default is json, so existing clients are unaffected.
Models: swift, and studio on an account with one voice in both modes
(GET /capabilities lists the modes per model for your key); studio on any other
key is answered with tts.invalid_request.
Text is synthesised segment by segment at sentence boundaries. Send
text.flush to force a partial segment out without ending the session.
A text.append whose content is a complete <speak> document is expanded
into passages immediately, so markup works in this mode too.
Every segment is an independent performance, so a reply sent sentence by
sentence can change slightly in pitch, pace and loudness between its
sentences. For one continuous performance, send the whole reply as "text"
in session.start, or add "passages":"turn" to session.start: appended
text is then held and rendered as one segment at text.flush (text left
unflushed for 2 s is rendered anyway). The default "passages":"sentence"
is unchanged; session.ready echoes the value in effect.
Server to client
{"type":"session.ready","request_id":"...","voice":"anna","language":"de-CH",
"dialect":"zurich","encoding":"pcm16","sample_rate_hz":24000,"frame_bytes":1024}
{"type":"audio.chunk","seq":1,"encoding":"pcm16","sample_rate_hz":24000,"data":"<base64>"}
{"type":"segment.done","characters":42,"duration_seconds":2.31,"cache":"miss"}
{"type":"mark","name":"after_ref","offset_seconds":7.15}
{"type":"session.complete","duration_seconds":7.94,"frames":48,"cache":{"hits":0,"misses":3}}
{"type":"error","error":{"code":"tts.unavailable","message":"...","request_id":"..."}}
Audio frames are a fixed frame_bytes in size apart from the final one.
Concatenate the decoded data values in seq order to reconstruct the
stream.
A key that keeps audio of fixed prompts
Only for a key for which this was enabled on written instruction (every other key's frames are
exactly as above): cache (use | refresh | off) and cache_ttl_s in session.start set
the session's mode, echoed in session.ready; cache on a text.append applies to that text,
on a text.flush to the text it releases; a passage made of text sent with different values
takes the most cautious (off before refresh before use). A passage served from the cache
arrives at once. segment.done then carries cache (off, hit, miss, refresh) with
cache_expires_at and cache_entry, and session.complete counts
{"hits":1,"misses":2,"refreshed":0,"off":3}. Without the cache switched on for your key these
fields are ignored and responses report "miss".
Responses
| Status | Description |
|---|---|
101 | Switching Protocols |
401 | Authentication required |
/tts/voicesList voices, languages and audio formatshttps://api.suisse-speech.ch/v1/tts/voices
The voice catalogue on its own, for clients that only need to populate a
picker. A subset of GET /capabilities.
Responses
| Status | Description |
|---|---|
200 | Voice catalogue Schema: object |
401 | Authentication required Schema: Error |
/tts/prewarmPrewarm phrases - only for a key that keeps audio of fixed promptshttps://api.suisse-speech.ch/v1/tts/prewarm
For every key without its own cache synthesised audio is processed in memory and not stored,
so prewarming stores nothing: every phrase comes back skipped, and nothing is synthesised or
billed, as before.
For a key that keeps audio of fixed prompts (enabled on written instruction), the phrases are
rendered with the given parameters and kept (cache defaults to use: only what is not kept
yet is rendered; refresh renders every phrase anew; off is refused). warmed is billed
once, already_warm is free. A kept phrase is served to requests with "cache": "use" and
exactly the same model, voice, language, dialect, speed, style and text.
Needs a live key: a sandbox key never reaches the engine, so there would be nothing to warm.
Request body · application/json
| Field | Type | Description |
|---|---|---|
model | "studio" | "swift" | Speech model to prewarm (studio or swift). default "studio" |
phrasesrequired | string[] | The passages to synthesise ahead of time. |
voice | string | default "anna" |
language | string | default "de-CH" |
dialect | string | default "standard" |
speed | number | default 1 |
style | string | |
cache | "use" | "refresh" | Only for a key that keeps audio; ignored on every other key. default "use" |
cache_ttl_s | integer | How long the phrases are kept; up to the key's min 60 |
Responses
| Status | Description |
|---|---|
200 | What was warmed Schema: object |
400 | No phrases, too many phrases, a sandbox key, or (on a key that keeps audio) |
429 | Rate limited or allowance exhausted |
/sttTranscribe an audio filehttps://api.suisse-speech.ch/v1/stt
Sync transcription for short audio (≤ 25 MB, ≤ 4 min). Pass audio as multipart/form-data
with field audio. Optional fields: lang (default de-CH), vocabulary
(JSON array of up to 200 strings to bias recognition). Longer audio is refused at
once with 413 stt.audio_too_large and a detail saying so: submit a recognition
job (POST /jobs, kind=stt) instead.
Multilingual recognition
lang takes one language code, several candidates separated by commas
(de-CH,fr-CH,en), or auto (short for de-CH,fr-CH,it,en). With more than
one candidate the spoken language is detected automatically, the transcript is
written in that language, and the result reports it in lang. The reported
value is always one of the candidates given, and is accepted unchanged as
language by POST /tts and /tts/stream. Name only the languages you expect:
fewer candidates means a steadier result on short utterances.
A candidate may carry a region or a dialect (fr-CH, de-CH-zurich) and is
reported back exactly so, which lets the choice of dialect travel with the code.
Recognition reports the language, not the dialect that was spoken.
Swiss German dialects and Standard German are both recognised under de-CH
and are written in Standard German. gsw, gsw-CH and gsw-CH-<dialect> are
accepted as Swiss German, like de-CH.
Swiss German or High German
With swiss_german_detection: true (alias variety_detection), recognition
also tells whether Swiss German (de-CH, de-CH-<dialect>, gsw, gsw-CH)
or High German (de, de-DE, de-AT, de-CH-hochdeutsch) was spoken: the
transcript carries variety (see the Variety schema), decided over the whole
recording, and with a code of each listed lang reports the candidate of the
variety heard. Undecided, lang is the first German candidate listed, as
without detection. Off unless sent as true; available on request (write to
info@suisse-it.ch and we enable it for your account). Only a recording recognised as German is checked: with other languages among the candidates, an English, French or Italian recording is neither checked nor delayed; a recording recognised as German is decided from its first seconds of speech. For German it costs time: for an utterance with less than about 4 s of speech the final can come up to about 1 s later (at most max_final_delay_ms in GET /capabilities); longer utterances are usually not delayed. Under about 1 s of speech (a single "ja" or "merci") nothing is decided and lang is the first German candidate listed.
Request body · multipart/form-data
| Field | Type | Description |
|---|---|---|
audiorequired | file | The recording, up to 25 MB and 4 minutes, in any common container (WAV at any rate, bit depth and channel count, MP3, Ogg/Opus or Vorbis, FLAC, M4A/AAC, WebM, AIFF, A-law/µ-law WAV, or a video file with an audio track). The content decides, not the file name. Headerless PCM is refused with 415; wrap it in a WAV header. Shorter than 0.1 s is refused with 400 stt.invalid_audio. |
lang | string | One code, comma-separated candidates ( default "de-CH" |
swiss_german_detection | boolean | Off unless true: omitted or false reports the first listed candidate, as before, also when |
variety_detection | boolean | The earlier name of |
conversation_id | string | One decision per call (since 1.8.0): your identifier of the call, 1-128 visible ASCII characters without spaces, the same on every request of the call. With max 128 chars |
vocabulary | string | Terms to bias recognition towards - names, places, product words. Either a JSON array of strings or a comma-separated list; at most 200 terms of 100 characters each. A value that is neither, a 201st term or a term over 100 characters is refused with 400 rather than ignored or applied in part. |
Responses
| Status | Description |
|---|---|
200 | Transcript Schema: Transcript |
400 | Invalid request Schema: Error |
401 | Authentication required Schema: Error |
413 | Audio too large: the upload is over 25 MB or the recording over 4 minutes ( Schema: Error |
415 | Unsupported audio format (the upload could not be decoded as audio) Schema: Error |
429 | Rate limit or daily audio budget ( Schema: Error |
503 | Service temporarily unavailable Schema: Error |
504 | Request timed out Schema: Error |
/stt/streamStreaming transcription (WebSocket)wss://api.suisse-speech.ch/v1/stt/stream
WebSocket endpoint at wss://api.suisse-speech.ch/v1/stt/stream. Upgrade with
the X-API-Key header (or ?api_key=sv_... query param).
Client → server messages
{"type":"config","lang":"de-CH","vocabulary":["..."]} // first message
{"type":"config","lang":"de-CH,de-DE","swiss_german_detection":true} // with Swiss German / High German detection
{"type":"config","lang":"de-DE,de-CH","swiss_german_detection":true,"conversation_id":"call-7f3c2a1e"} // ... decided once per call
{"type":"config","lang":"de-CH","finalize_ack":true} // voice bots: finalize commits at once (the default), acknowledged
{"type":"config","lang":"de-CH","finalize_mode":"pause"} // live captions: finalize waits for the next pause
{"type":"audio.append","data":"<base64 PCM16 16kHz mono>"} // chunks
{"type":"finalize"} // optional: commit what has been heard
{"type":"audio.end"} // signal end
Binary frames are also accepted as raw PCM16 audio; a frame need not end on a
sample boundary, and audio sent before config (or before session.ready) is
kept and recognised in order. The subprotocol reply is always suisse.v1; the
key carried in Sec-WebSocket-Protocol is never echoed. The upgrade response
carries X-Suisse-Environment, and X-Suisse-Key-Status on a superseded key.
Server → client messages
{"type":"session.ready","request_id":"...","finalize_mode":"immediate"} // the mode in force; "finalize_ack":true when acknowledged
{"type":"transcript.partial","text":"...","words":[...],"lang":"de-CH","duration_s":1.2}
{"type":"transcript.final","text":"...","words":[...],"lang":"de-CH","duration_s":3.4}
{"type":"transcript.final","text":"...","words":[...],"lang":"de-CH","duration_s":5.1,"forced":true}
{"type":"transcript.final","text":"...","lang":"de-CH","variety":{"spoken":"swiss_german","confidence":0.97,"decided":true},"words":[...],"duration_s":3.4}
{"type":"finalize.done","request_id":"...","committed":true,"ms":38} // finalize_ack: one per finalize, after its finals
{"type":"session.complete","request_id":"..."}
{"type":"error","code":"stt.upstream_unavailable","message":"...","request_id":"..."}
Partial events are emitted every 250 ms; the first one as soon as the
recogniser has heard a word, which can be before the first tick. A final always
ends on a word boundary (unless you send finalize in the middle of a word).
The normal close code is 1000. A config naming a language
the catalogue does not list, or a malformed vocabulary, is answered with an
error frame stt.invalid_request (with detail) before the socket closes.
Getting finals sooner
Without a commit a word is final about five to six seconds after it was
spoken. A commit makes final what has been heard so far: send finalize
(below), or let an option in config commit for you (all off by default):
"finalize_on_pause": truecommits at pauses in speech: as soon as the speaker pauses after a sentence the recogniser holds to be complete (typically 0.7 s after the sentence ends), or after 0.8 s of pause. For live captions and live translation of continuous speech.finalize_pause_ms(300–2000, default 800) sets that pause: shorter commits more often, with more fragments cut inside a sentence (eachforced: true); out of range is refused withstt.invalid_request. Measured on a continuously speaking preacher: 800 ms → a word final 5.2 s after it was spoken (median), every sentence end kept; 600 ms → 4.7 s, 12 of 15 sentence ends; 400 ms → 2.5 s, 3 of 15 (the commit comes before the recogniser has decided the sentence is over). For a pipeline that reads punctuation, 600 is the practical floor."endpointing": true(withendpoint_delay_ms, 500–3000) commits at the end of a turn in a dialogue. It does not fire inside a monologue.
{"type":"finalize"} asks for one commit. "finalize_mode" in config says when
it is applied (since 1.8.0):
"immediate"(the default): at once, whether or not your audio goes on, and the finals of everything heard so far follow as soon as the recogniser confirms the commit - 0.03 s after thefinalize(median; 0.07 s at the 95th percentile), as afteraudio.end(2,400 telephone turns, measured on the service without the network between you and us). For voice bots and push-to-talk: send it at your own end of speech, after about 200 ms of silence; counted from the end of speech, add that silence (or, withendpointing, the recogniser's end-of-turn decision). A finalize in the middle of a word cuts the word there. At most one commit a second: a finalize within a second of the previous one is applied the moment that second is over; repeated requests before it is applied are one commit."pause": at the next pause in which the sentence is complete, never in the middle of a word or a sentence; repeated requests before that are one request. The pause is heard in the audio you send: keep sending audio afterfinalize(silence is fine) - if your audio stops, the commit is applied one second later. For live captions and live translation, which finalize in the middle of speech.
A final produced by a commit carries "forced": true. Its punctuation is the
recogniser's reading of the speech: a sentence end it adds only because of the
commit is removed - except with "immediate" after at least 200 ms of
silence, where the commit keeps it, as audio.end would. "finalize_ack": true (with "immediate") answers every finalize with exactly one
{"type":"finalize.done","request_id":"...","committed":true,"ms":38} after
the finals of its commit: committed: false when there was nothing left to
commit or audio.end took it (then before session.complete); ms is the
time from your finalize to this event on our side. Without the flag no such
event is sent. session.ready names the mode in force (finalize_mode, and
finalize_ack: true when the events will come). Any other value of
finalize_mode is ignored (the default applies, as without the field). With
Swiss German detection a German final can still wait for its variety (with
conversation_id only until the call is decided), and finalize.done comes
after it: nothing overtakes a final.
Knowing the session is working
"status_interval_ms": 5000 in config (1000–60000, off by default) sends
{"type":"session.status","request_id":"...","audio_ms":184000,"processed_ms":183920,"last_word_ms_ago":1250,"stalled":false}
at that interval: audio received, audio the recogniser has worked through
(it keeps up to within about a second, also on silence and music), time since
the last word (null before the first), and stalled: true when audio
arrives but the recogniser has not moved through it for 5 s - reconnect then.
No words while processed_ms keeps up means no speech is heard in the audio.
Out of range is refused with stt.invalid_request.
Multilingual recognition
lang in config takes one code, comma-separated candidates
("de-CH,fr-CH,en") or "auto", exactly as on POST /stt. Every
transcript.partial and transcript.final then carries the language detected
for that stretch of speech in lang, so a change of language during a session
is visible as it happens.
Swiss German or High German
With "swiss_german_detection": true in config (off unless sent), every German
transcript.final carries variety (see the Variety schema), and with a Swiss
German and a High German candidate (for example "de-CH,de-DE") lang reports
the candidate of the variety heard. Partials carry no variety (they report
the first listed German candidate); the variety is decided on finals, and while
a German final waits for it, the events after it wait too (the order is kept). Only utterances recognised as German are checked: with other languages among the candidates (for example "de-CH,de-DE,en,fr-CH,it-CH"), English, French and Italian utterances are never checked; their finals wait only behind a pending German final. For German it costs time: for an utterance with less than about 4 s of speech the final can come up to about 1 s later (at most max_final_delay_ms in GET /capabilities); longer utterances are usually not delayed. With conversation_id (below) the wait ends with the first decision of the call. Under about 1 s of speech (a single "ja" or "merci") nothing is decided and lang is the first German candidate listed. "swiss_german_detection" (alias
"variety_detection") in config has the same meaning as on POST /stt.
Available on request: write to info@suisse-it.ch and we enable it for your account;
without it, a config with "swiss_german_detection": true is answered with an
error frame stt.invalid_request (with detail) before session.ready. Accuracy:
see the Variety schema.
One decision per call (since 1.8.0): send the same "conversation_id" (1-128
visible ASCII characters, no spaces) in the config of every stream of a call. Until a
German final of the call is decided, each is judged on its own (variety.source: "utterance"); the first decided one decides the call, and every later German final of
the call - in this stream or any later one, or a POST /stt with the same id - carries
that variety at once, decided: true, source: "call", without the delay detection
otherwise adds. While the decision is new, the next German utterances are checked in
the background (nothing waits): one agreeing settles it, two disagreeing correct it
once, early in the call. A whole number counts as its digits; any other value is
ignored, never refused (decided per utterance, no source). Recommended for voice
bots: until the call is decided, list first the variety you would rather answer in when
unsure (for example "de-DE,de-CH").
Responses
| Status | Description |
|---|---|
101 | Switching Protocols |
401 | Authentication required |
/agent/streamRealtime voice agent (WebSocket)wss://api.suisse-speech.ch/v1/agent/stream
WebSocket endpoint at wss://api.suisse-speech.ch/v1/agent/stream (since 1.9.0): speech in,
speech out. Our recognition hears the user; the agent answers in the voice, language and
dialect of the session, follows your instructions and calls your tools. Authenticate as
on the other sockets: the X-API-Key header, Sec-WebSocket-Protocol: suisse.v1, key.<api key>
or ?api_key=. The subprotocol reply is always suisse.v1; the key is never echoed.
A browser or app uses an ephemeral token from POST /agent/tokens instead of the key, in the
same places (key.<token>, ?api_key=<token>); it is consumed by the handshake.
Released per account. A key of an account without the voice agent gets
{"type":"error","code":"agent.not_enabled",...} and the close code 1008. GET /capabilities
says it in agent.enabled_for_your_account. Sandbox keys (sv_test_) get a synthetic session
with the same events (a synthetic transcript, a short test tone as the answer, "sandbox": true
in every event), never billed.
The client keeps the agent's voice out of its microphone (in a browser: open the microphone with getUserMedia's cancellation of played-back audio switched on, or use a headset): otherwise the agent hears itself and interrupts itself.
Client → server messages
{"type":"session.start", // exactly once, first
"instructions":"You are the reception of ...", // up to 16,000 characters
"language":"de-CH","dialect":"zurich", // GET /capabilities agent.languages / agent.dialects
"voice":"anna", // agent.voices; default anna
"greeting":"Grüezi, wie chan ich Ihne hälfe?", // optional: the agent speaks first, word for word
"input":{"encoding":"pcm16","sample_rate_hz":16000},
"output":{"encoding":"pcm16","sample_rate_hz":24000,"transport":"json"},
"turn_taking":{"mode":"auto","interruptions":true,"patience":"short","idle_prompt_s":10,"idle_end_s":45},
"tools":[{"name":"book_slot","description":"...","parameters":{"type":"object","properties":{}}}],
"vocabulary":["Suisse IT"],"max_duration_s":900,"transcripts":true,
"languages":["de-CH","fr-CH","it-CH"], // up to 4: the user may switch between them
"pronunciations":[{"text":"SIT","say":"Suisse I T"}],
"speed":1.0, // 0.85–1.20
"end_conversation":true, // the agent may end the conversation (see Call control)
"transfer_targets":[{"name":"Support","description":"Technical questions"}]}
{"type":"audio.append","data":"<base64 audio in the input format>"} // or binary frames
{"type":"input.commit"} // turn_taking.mode manual: the user is done
{"type":"text.input","text":"..."} // a user message as text
{"type":"context.add","text":"..."} // background knowledge, no reply
{"type":"reply","instructions":"..."} // the agent speaks now (instructions optional)
{"type":"say","text":"..."} // the agent says this text word for word
{"type":"tool.result","call_id":"tc_...","output":{...}} // a string or any JSON
{"type":"interrupt"} // stop the agent now
{"type":"playback.mark","reply_id":"rp_...","played_ms":1840} // optional: what was played of that reply
{"type":"config.update","instructions":"...","tools":[...]}
{"type":"session.end"}
Defaults (each field of session.start is optional): language de-CH, dialect standard
(de-CH only: standard, zurich, bern, basel, luzern), voice anna; input pcm16 at 16000 Hz;
output pcm16 at 24000 Hz, transport json; turn_taking mode auto, interruptions true,
patience short, idle_prompt_s 10 (5–60), idle_end_s 45 (15–300, longer than idle_prompt_s);
max_duration_s 900 (60–1800); transcripts true; languages the session language only (up
to 4 of agent.languages, the session language first; without language the first listed);
pronunciations none (up to 100, text up to 64, say up to 128 characters); speed 1.0
(0.85–1.20, on top of the voice's own tempo); end_conversation true; transfer_targets none
(up to 10, name up to 64, description up to 256 characters). Encodings pcm16, mulaw,
alaw (mulaw and alaw 8000 Hz only); sample rates 8000, 16000, 24000, 48000.
A field this protocol does not define is refused with agent.invalid_request; detail
names its position, never its name or value.
Server → client messages
{"type":"session.ready","request_id":"req_...","session_id":"req_...","voice":"anna","language":"de-CH","dialect":"standard",
"input":{"encoding":"pcm16","sample_rate_hz":16000},
"output":{"encoding":"pcm16","sample_rate_hz":24000,"transport":"json","frame_ms":40,"frame_bytes":1920},
"turn_taking":{"mode":"auto","interruptions":true,"patience":"short","idle_prompt_s":10,"idle_end_s":45},
"languages":["de-CH"],"speed":1.0,"end_conversation":true,"transfer_targets":0,"pronunciations":0,
"tools":1,"transcripts":true,"max_duration_s":900,"limited_by":"max_duration_s"}
{"type":"user.speech_started"} {"type":"user.speech_stopped"}
{"type":"user.transcript.partial","text":"..."} {"type":"user.transcript.final","text":"...","language":"de-CH"}
{"type":"reply.started","reply_id":"rp_..."}
{"type":"audio.chunk","reply_id":"rp_...","seq":1,"data":"<base64, 40 ms>"} // transport json; binary: raw frames
{"type":"reply.transcript.delta","reply_id":"rp_...","text":"..."}
{"type":"reply.audio_done","reply_id":"rp_..."}
{"type":"reply.done","reply_id":"rp_...","status":"completed","transcript":"..."} // completed | interrupted | cancelled | failed; transcript only with transcripts true
{"type":"audio.clear","reply_id":"rp_..."} // barge-in: empty the playback buffer now
{"type":"tool.call","call_id":"tc_...","name":"book_slot","arguments":{"day":"2026-11-03"},"reply_id":"rp_..."}
{"type":"transfer.requested","request_id":"req_...","target":"Support","reason":"...","reply_id":"rp_..."}
{"type":"session.end_requested","request_id":"req_...","reason":"goodbye"} // goodbye | user_request | closing_confirmed | idle
{"type":"session.warning","code":"agent.duration_ending","seconds_left":60}
{"type":"session.status","request_id":"req_...","state":"listening","recognition":"ok","connected_s":60,"remaining_s":840,"open_tool_calls":0}
{"type":"error","code":"agent.invalid_request","message":"...","detail":"...","request_id":"req_..."}
{"type":"session.complete","request_id":"req_...","duration_s":184.2,"billable_seconds":184.2,
"end_reason":"goodbye", // after session.end_requested; "restart" when the service restarts; "unavailable" when it cannot continue
"transcript":[{"role":"agent","text":"Grüezi ...","at_ms":420},{"role":"user","text":"...","at_ms":3100}]}
Every audio frame holds 40 ms in the output format (frame_bytes; the last frame of a reply
may be shorter). With transport binary, reply.started comes before a reply's first frame and
reply.audio_done after its last. After audio.clear no frame of that reply follows.
Pacing. Audio leaves on the service's own clock: the first up to 200 ms of a reply at
once, then a frame every 40 ms at about 1.25 times realtime, never more than about 1.5 s
ahead of your playback. Your playback buffer stays small, and audio.clear takes effect at
once. A reply's events keep their order with its audio: reply.audio_done and reply.done
come after its last frame, and the next reply's reply.started after the previous reply's
reply.done. user.speech_started and user.speech_stopped also come on this 40 ms clock.
While more than about 1.5 s of audio still waits to be sent, the next reply starts only
once it has (it is not lost). Transcript deltas that wait behind audio may arrive merged
into one. A reply's audio is checked against its own transcript before it is sent, so its
first frame comes a moment after the first words of the transcript are ready.
Transcripts. With transcripts false there are no user.transcript.* and no
reply.transcript.delta events, reply.done carries no transcript and session.complete
none either.
Turn-taking. auto: the agent answers when the user has finished (patience short, normal,
long: how soon). manual: send input.commit when the user is done. With interruptions true
the user can interrupt the agent (two words, a stop word such as "stopp" or "moment", or longer
speech; a short "mhm" does not): you get audio.clear and reply.done with status
interrupted. playback.mark makes the cut exact when your playback lags. A mark counts
for its own reply only; a mark for a reply that no longer plays (interrupted, cleared, or
long finished) is accepted and ignored, never an error. Only a mark that goes back or
exceeds the audio sent for a reply still playing is agent.invalid_request.
Tools. tool.call.arguments is always a JSON object. Answer every call_id once with
tool.result within 30 s; later, the agent tells the user it did not work and the call is
closed (tool.result then: agent.invalid_request, detail "call expired"). A call stays
valid after an interruption.
Tool parameters use a subset of JSON Schema: type (object, string, number, integer,
boolean, array), properties, required, enum, description, items, minimum,
maximum, maxLength, nested at most 6 levels. The keywords of everyday strict schemas
additionalProperties, title, default, examples, $schema, minLength, minItems
and maxItems are accepted and ignored (removed before the schema is used; validate your
tool's input yourself). Anything else (for example oneOf, anyOf, allOf, $ref,
pattern, format) is refused with agent.invalid_request; detail names its position.
end_conversation and transfer_to_human are reserved names. A call of a reply that was
interrupted or cut never reaches you.
Call control. Two built-in functions the agent calls itself:
end_conversation (with end_conversation true, the default) ends only on the user's own
words: a goodbye, an explicit wish to end, or a clear no to the agent's closing question;
otherwise the agent asks first. Then session.end_requested, the goodbye plays (estimated
from the audio sent, or your playback.mark), session.complete and close 1000.
transfer_to_human (only with transfer_targets) sends transfer.requested with the
target's name and a short reason; the agent says a short handover line and the session
goes on: your telephony makes the transfer and then ends the session.
Silence (turn_taking mode auto). The clock starts with the first exchange and runs only
while nobody speaks, nothing is played and no tool call is open: after idle_prompt_s the
agent asks once, in the user's language, whether they are still there; after idle_end_s
it says goodbye and the session ends (session.end_requested, reason idle).
Languages. With several languages recognition listens for all of them,
user.transcript.final carries the language heard, and the agent answers in the user's
language, one language per reply; for Swiss German it keeps the chosen dialect.
Transcript. With transcripts true, session.complete carries transcript: every user
turn and what the agent actually played (an interrupted reply up to where it was heard),
with at_ms since session.ready (at most 1,000 entries and 200,000 characters, then
transcript_truncated: true).
session.status comes every 60 s and when recognition changes: state listening,
thinking or speaking; recognition ok or degraded (then the agent answers from the audio
alone, without user.transcript.*); connected_s, remaining_s, open_tool_calls.
Warnings (session.warning, the session goes on): agent.duration_ending 60 s before
max_duration_s; agent.entitlement_exhausted when the credit runs out (the session ends
30 s later or at max_duration_s when the credit set it); agent.usage_limit when the
session's usage protection applies (it ends 30 s later); agent.audio_dropped when your
connection reads audio too slowly (the excess is dropped), or a single reply runs more than
about 30 s ahead of your playback: then audio.clear (only if audio of that reply already
reached you) and reply.done with status interrupted follow, and the conversation
continues from what the listener heard.
Close codes: 1000 normal; 1001 service restarting (reconnect; the session is ended
properly first: session.complete with end_reason "restart", its seconds booked); 1009 a
frame larger than the socket's ceiling (65 KB; standard WebSocket "message too big", the
frame is not read); 1008 policy (not
enabled, a malformed session.start, no session.start within 10 s, more than 50 refused
frames within 10 s: over the frame rate, too large or unreadable); 1011 error
(agent.unavailable; a running session first gets session.complete with end_reason
"unavailable"); 1008 also for agent.forbidden (a locked token's field changed);
4000 agent.duration_limit; 4001 agent.timeout (no audio and no
message for 120 s); 4002 agent.usage_limit; 4003 agent.entitlement_exhausted. A
session that ends with an error sends the error frame first.
Limits: instructions 16,000 characters; up to 32 tools, 16,000 characters together,
instructions and tools 24,000; tool names ^[a-zA-Z0-9_-]{1,64}$, descriptions 1,024
characters, parameters nested at most 6 levels; a text frame 64 KB and a binary frame 64 KB
(a larger frame up to 65 KB gets agent.invalid_request or agent.invalid_audio, above
that the socket closes with 1009); at most 100 frames per second, text and binary
together, every frame counted (also one that is too large or cannot be read; the excess is
dropped); audio at most 1.2 times realtime; 20 control messages per second (a frame that
cannot be read counts as one); reply, say and text.input together 30 per minute; 10
config.update and 50 context.add per session. Errors that do not end the session come
at most once per code per second; further ones in that second are dropped silently.
Billing: connected seconds from session.ready to the end, per second, no minimum; a
start that fails before session.ready costs nothing. The account's remaining credit can
shorten max_duration_s (limited_by: "credit"): each session holds the credit for its
limit, so sessions running at the same time share the balance and never overdraw it; under
one minute of credit left for a new session it is refused with agent.entitlement_exhausted.
If the balance of a prepaid account cannot be read or held at the start, the session is
refused with agent.unavailable (close 1011): retry shortly. A session is billed at the price
in force when it started.
Responses
| Status | Description |
|---|---|
101 | Switching Protocols |
401 | Authentication required (agent.unauthenticated): no valid key, or a token that is unknown, used or expired |
429 | No capacity, the plan's simultaneous sessions ( |
503 | A token was offered while tokens are unavailable (agent.unavailable) |
/agent/tokensEphemeral token for the voice agenthttps://api.suisse-speech.ch/v1/agent/tokens
Your server mints a short-lived token with its API key and hands it to a browser or app,
which opens /agent/stream with it instead of the key (Sec-WebSocket-Protocol: suisse.v1, key.<token> or ?api_key=<token>). A token is sat_ followed by 32 URL-safe characters,
valid for 60 seconds and for one handshake, for the account and environment of the key that
minted it (a sandbox key mints a sandbox token). It is stored only as a hash.
Optional JSON body (Content-Type: application/json): session presets session.start
fields; the client's own session.start is applied over them. The endpoint is strict: only
session and lock at the top level, no query parameters, and a body that is not JSON is
refused; the preset is checked with the rules of session.start for the fields it carries
(an unknown field, a wrong type, an unknown voice, language or dialect), all with
agent.invalid_request (400) at minting, not later. With lock: true the preset
instructions, tools, voice, language, dialect and greeting are fixed (absent ones
stay at their defaults): a session.start that sends another value is refused with
agent.forbidden (close 1008), and config.update and reply.instructions are refused
with agent.forbidden during the session.
Parameters
| Name | Type | Description |
|---|---|---|
X-API-Keyrequired | string · header |
Request body · application/json
| Field | Type | Description |
|---|---|---|
session | object | session.start fields (without type) |
lock | boolean | default false |
Responses
| Status | Description |
|---|---|
201 | The token Schema: object |
400 | agent.invalid_request: a body or session field that is not accepted (by position) |
401 | agent.unauthenticated |
403 | agent.not_enabled: the voice agent is not released for this account |
429 | agent.rate_limited |
503 | agent.unavailable: tokens cannot be issued just now; retry shortly |
/jobsList recent jobs for this keyhttps://api.suisse-speech.ch/v1/jobs
Parameters
| Name | Type | Description |
|---|---|---|
limit | integer · query |
Responses
| Status | Description |
|---|---|
200 | Jobs, newest first Schema: object |
/jobsSubmit an asynchronous job (synthesis or recognition)https://api.suisse-speech.ch/v1/jobs
For text long enough that holding a connection open for the whole
synthesis is the wrong shape. Acknowledged in milliseconds; the audio is
collected later from /jobs/{id}/audio.
A synthesis job (JSON) takes the fields of POST /tts, model included.
A recognition job (multipart, kind=stt) takes the fields of POST /stt
and answers with the recognition error codes (stt.*).
Everything is validated at submission, so a bad voice or malformed
markup is a 400 here rather than a failure discovered later.
Results are kept for 48 hours. Up to 200 jobs may be queued per key. Jobs queue behind interactive traffic and are metered identically.
Request body · application/json
Request body · multipart/form-data
| Field | Type | Description |
|---|---|---|
kind | "stt" | default "stt" |
audiorequired | file | The recording, up to 200 MB and 3 hours, in any common container (as POST /stt takes). |
lang | string | As on POST /stt; a code the catalogue does not know is refused here, at submission. default "de-CH" |
swiss_german_detection | boolean | As on POST /stt: Swiss German or High German, decided over the recording and reported in the transcript; only a recording recognised as German is checked. Refused at submission when the account does not have it (available on request). |
vocabulary | string | Terms to favour, as a JSON array or a comma-separated list: up to 200 terms of at most 100 characters. |
callback_url | string |
Responses
/jobs/{id}Job status, and the result when it has finished/jobs/{id}/audioCollect the audio of a finished synthesis jobhttps://api.suisse-speech.ch/v1/jobs/{id}/audio
Parameters
| Name | Type | Description |
|---|---|---|
idrequired | string · path |
Responses
| Status | Description |
|---|---|
200 | The audio Schema: file |
400 | Not finished, not found, or the result has expired Schema: Error |
/capabilitiesDiscover everything the speech API acceptshttps://api.suisse-speech.ch/v1/capabilities
Runtime discovery. Returns every voice, language, dialect, audio format, parameter range and error code the service accepts, plus the calling key's own quota for the current day.
Read this instead of hard-coding values: a new voice or language appears here without any client change.
Responses
| Status | Description |
|---|---|
200 | Capability document Schema: Capabilities |
401 | Authentication required Schema: Error |
/usageWhat this key has consumedhttps://api.suisse-speech.ch/v1/usage
Read from the billing ledger, not from the rate limiter's expiring counters, so these are the same numbers an invoice is built from, in the same unit. A customer billed by the second is entitled to reconcile continuously rather than at month end.
Sandbox traffic is reported separately and is never billed.
Parameters
| Name | Type | Description |
|---|---|---|
from | string · query | Start of the window. Defaults to the start of the current month. |
to | string · query | End of the window. Defaults to now. |
granularity | "total" | "day" · query | Add |
Responses
| Status | Description |
|---|---|
200 | Consumption for the window Schema: object |
400 | Invalid window |
401 | Missing or invalid key |
/tts/cacheDelete everything this key keepshttps://api.suisse-speech.ch/v1/tts/cache
Deletes every kept rendering of this key at once: they are unreadable from that moment and
removed from disk. The key keeps its setting; renderings requested with "cache": "use"
afterwards are kept afresh. Recorded, without content, in the account's activity. Works on
every key (deleted is 0 when nothing is kept).
Responses
| Status | Description |
|---|---|
200 | What was deleted Schema: object |
401 | Missing or invalid key |
/tts/cache/{id}Delete one kept renderinghttps://api.suisse-speech.ch/v1/tts/cache/{id}
The cache_entry of a response (ce_ and 32 hexadecimal digits). deleted is 0 when it is not (or no longer) kept for this key.
Parameters
| Name | Type | Description |
|---|---|---|
idrequired | string · path |
Responses
| Status | Description |
|---|---|
200 | What was deleted Schema: object |
400 | Not a cache entry id |
401 | Missing or invalid key |
/tts/cacheWhat this key keeps (counts and expiries, never texts)https://api.suisse-speech.ch/v1/tts/cache
Whether this key keeps audio of fixed prompts (enabled per key on written instruction), its default mode and expiry, and what it holds: the number of kept renderings, their size and audio seconds, and when the next and the last of them expire. Never a text, never a list of entries.
Responses
| Status | Description |
|---|---|
200 | The key's cache Schema: object |
401 | Missing or invalid key |
Schemas
Word| Field | Type | Description |
|---|---|---|
wrequired | string | The word. |
startrequired | number | Start time in seconds from the audio start. |
endrequired | number | End time in seconds. |
confrequired | number | Confidence in [0,1]. min 0 · max 1 |
Transcript| Field | Type | Description |
|---|---|---|
textrequired | string | |
langrequired | string | Language of the transcript. When the request named several candidate languages (or e.g. "de-CH" |
wordsrequired | Word[] | |
duration_srequired | number | Audio duration in seconds: the length of the recording, silence included, which is what is billed. |
variety | Variety | Swiss German (dialect) or High German, decided from the audio. Present only when the request sent e.g. {"spoken":"swiss_german","confidence":0.97,"decided":true} |
sandbox | boolean | Present, and true, only when a sandbox key answered: the transcript is synthetic and nothing was recognised. A real transcript never carries this key. |
VarietySwiss German (dialect) or High German, decided from the audio. Present only when the request sent swiss_german_detection: true and German was heard. Only speech recognised as German is checked: with other languages among the candidates, English, French and Italian are never checked (their finals wait only behind a pending German final, to keep the order). Available on request: write to info@suisse-it.ch and we enable it for your account. Measured per utterance on telephone calls of 202 real callers: Swiss German (dialect) reported as swiss_german in 92 %, High German of speakers from Germany and Austria as high_german in 88 %; High German spoken by Swiss speakers as swiss_german about half the time.
| Field | Type | Description |
|---|---|---|
spokenrequired | "swiss_german" | "high_german" | The variety heard, also when undecided (then a lean). High German spoken with a Swiss accent counts as high_german, but is reported as swiss_german about half the time. |
confidencerequired | number | How sure the decision is, from 0.5 to 1. min 0.5 · max 1 |
decidedrequired | boolean | false: too little speech (for example a single word such as "ja" or "merci") or unclear audio. |
source | "utterance" | "call" | Only on a request that sent |
Error| Field | Type | Description |
|---|---|---|
errorrequired | object |
Voice| Field | Type | Description |
|---|---|---|
idrequired | string | Value to pass as e.g. "anna" |
namerequired | string | e.g. "Anna" |
genderrequired | "female" | "male" | "neutral" | |
timbrerequired | string | e.g. "warm" |
pacerequired | string | e.g. "medium" |
descriptionrequired | string | |
best_forrequired | string[] | |
languagesrequired | string[] |
Dialect| Field | Type | Description |
|---|---|---|
idrequired | string | Value to pass as e.g. "zurich" |
namerequired | string | e.g. "Züridütsch" |
region | string | e.g. "Zürich" |
Language| Field | Type | Description |
|---|---|---|
coderequired | string | e.g. "de-CH" |
namerequired | string | e.g. "Swiss German" |
statusrequired | "verified" | "supported" |
|
dialectsrequired | Dialect[] | |
default_dialect | string | e.g. "standard" |
AudioFormat| Field | Type | Description |
|---|---|---|
idrequired | "wav" | "mp3" | "opus" | "pcm16" | "mulaw" | "alaw" | |
mimerequired | string | e.g. "audio/mpeg" |
extensionrequired | string | e.g. "mp3" |
description | string | |
sample_rates_hzrequired | integer[] | |
default_sample_rate_hzrequired | integer | e.g. 24000 |
streamablerequired | boolean |
SynthesisRequest| Field | Type | Description |
|---|---|---|
model | "studio" | "swift" | Speech synthesis model:
default "studio" |
textrequired | string | The text to speak. Rendered verbatim; never interpreted as an instruction. max 8000 chars |
voice | string | Voice id from default "anna" |
language | string | Language code. default "de-CH" |
dialect | string | Variety within the language.
default "standard" |
speed | number | Speaking rate multiplier (0.5 = half speed, 1.0 = normal pace, 2.0 = double speed). swift applies it exactly. studio has the voice perform it, so the pace changes audibly but not by the exact factor (measured: 1.5 about 1.25 times faster, 0.7 about 0.8 times as fast). default 1 · min 0.5 · max 2 |
style | string | Free-text delivery prompt (up to 600 chars) steering tone, emotion, and pacing. Examples:
max 600 chars |
format | "wav" | "mp3" | "opus" | "pcm16" | "mulaw" | "alaw" | mp3 is constant bitrate: 96 kbit/s at 44.1 and 48 kHz, 80 kbit/s at 32 kHz. default "mp3" |
sample_rate_hz | integer | Output sample rate. Permitted values depend on |
output | "audio" | "json" |
default "audio" |
cache | "off" | "use" | "refresh" | The key's own cache, for keys for which keeping audio was enabled on written instruction
( |
cache_ttl_s | integer | How long a new rendering is kept, in seconds: from 60 to the key's min 60 · max 2592000 |
SynthesisResult| Field | Type | Description |
|---|---|---|
request_idrequired | string | |
voicerequired | string | e.g. "anna" |
languagerequired | string | e.g. "de-CH" |
dialect | string | e.g. "zurich" |
speed | number | e.g. 1 |
charactersrequired | integer | Characters of spoken text, after markup has been expanded. |
passages | integer | Number of separately rendered passages. 1 for plain text. |
cache | "miss" | "off" | "hit" | "partial" | "refresh" |
|
cache_expires_at | string | Only for a key that keeps audio, when the answer was served from or kept in its cache: when that rendering expires (the soonest, for a document of several passages). |
cache_entry | string | Only for a key that keeps audio: the kept rendering, for DELETE /tts/cache/{id}. Opaque. e.g. "ce_4f1c9a2b7d3e5f60718293a4b5c6d7e8" |
cache_entries | string[] | Instead of |
marks | object[] | Present when the document contained elements. |
audiorequired | object |
Job| Field | Type | Description |
|---|---|---|
idrequired | string | e.g. "job_50f665b6efff3fbfb6b58fdf" |
kindrequired | "tts" | "stt" | |
statusrequired | "queued" | "running" | "succeeded" | "failed" | |
environment | "live" | "sandbox" | |
created_atrequired | string | |
started_at | string | |
finished_at | string | |
audio_seconds | number | |
result_expires_at | string | |
audio | object | Present once the job has succeeded. |
callback | object | Delivery state, when the job carried a callback_url. |
cache | "off" | "hit" | "partial" | "miss" | "refresh" | Only for a synthesis job submitted with a key that keeps audio of fixed prompts: what the key's cache did, as POST /tts reports it. With |
cache_expires_at | string | |
cache_entry | string | |
cache_entries | string[] | |
marks | object[] | Present once a synthesis job has succeeded and its document contained elements: the same offsets POST /tts reports. |
transcript | object | Present on a succeeded recognition job read with GET /jobs/{id}, the same shape as POST /stt answers with; the list (GET /jobs) carries has_transcript instead. |
has_transcript | boolean | Recognition jobs: whether the transcript is still held (results are kept for 48 hours). |
error | object |
CapabilitiesRuntime description of the whole service. Generated from the same catalogues the request handlers validate against, so it cannot drift from the implementation.
| Field | Type | Description |
|---|---|---|
api | object | |
authentication | object | |
text_to_speech | object | |
speech_to_text | object | |
limits | object | |
capacity | object | Concurrency ceilings this deployment enforces, and how much is in flight. |
cache | object | The calling key's own cache. For a key without it (every key unless enabled on written
instruction): |
usage_today | object | Where the calling key stands against its daily budgets, both directions, in UTC days. |
errors | object[] |