Authentication and keys
https://api.suisse-speech.ch/v1 · X-API-KeyEvery endpoint requires an API key.
X-API-Key: sv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WebSocket endpoints accept the same header. Browsers cannot set headers on a WebSocket handshake, so they may pass the key in the subprotocol field instead:
new WebSocket(url, ['suisse.v1', 'key.' + apiKey])
A query parameter (?api_key=) is still accepted for compatibility, but avoid
it: query strings end up in proxy logs and browser history. Ours are masked, but
yours may not be.
Keys are per account. Treat one as a credential: it carries the account’s whole budget, and it is not scoped to a single endpoint. Never put a live key into code that runs in a browser or an app you ship to others.
Rotation
Rotating a key does not cut off the old one. The superseded key keeps working for seven days, so deploying a new secret is a change window rather than an outage. Every response made with the old key says when it stops:
X-Suisse-Key-Status: superseded; stops working 2026-09-10T20:42:49.111Z
Watch for that header in your logs: it is how you find the last service still holding the old secret before it expires on its own. On a WebSocket the same header comes back on the upgrade response.
If a key has leaked, do not wait for the window: ask us to end the overlap immediately. The new key is unaffected.
Environments
The environment is carried by the key, not by the URL. One base URL, one integration, and a key that says what it is on its face:
| key | environment | |
|---|---|---|
sv_… |
live | billed, counts against your allowance and your production limits |
sv_test_… |
sandbox | not billed, no allowance consumed; always answers with synthetic test audio and transcripts, no speech engine involved; its own daily budget and its own small concurrency slice |
Every response says which one answered:
X-Suisse-Environment: sandbox
Point your test suite at a sandbox key and a misconfigured environment can never reach live traffic or appear on an invoice. A sandbox key cannot be confused for a live one in a config file, a log line or a screenshot.
Additional keys. Besides the account’s own live and sandbox key you can create further keys in the console (API keys → Create API key), one per system or team, each with its own name, live or sandbox. They authenticate exactly like the account’s own keys, share the account’s limits and credit, and can be revoked one by one without touching the others. A key is shown once, when it is created; we store only a fingerprint of it.
Sandbox traffic still appears in GET /v1/usage, reported separately and
marked "billed": false.
Synthetic mode: free, instant, deterministic
A sandbox key always gets output that never reaches a speech engine; real voices and real recognition need a live key, and live traffic is billed. The header below is still accepted from integrations that send it, but it is no longer needed:
X-Suisse-Sandbox-Mode: synthetic
You get audio and transcripts that are structurally identical to the real thing (same formats, same framing, same headers, same durations) produced locally at no cost to either side. Built for CI, where a run is checking that your integration works, not that the speech is good.
- Deterministic. The same request returns byte-identical audio, so assert on a hash rather than on “roughly this long”.
- Realistic timing. Duration follows the same characters-per-second model the meter bills with, so your buffering and playback logic sees plausible numbers.
- Obviously not speech. Nobody will mistake a green CI run for a working integration against the real engine, and a synthetic transcript says so in its own text.
curl -X POST https://api.suisse-speech.ch/v1/tts \
-H "X-API-Key: $SANDBOX_KEY" \
-H 'Content-Type: application/json' \
-d '{"text":"CI run 4711","voice":"anna","format":"mp3"}' --output ci.mp3
Asking for synthetic mode on a live key is refused with
400 tts.invalid_request rather than quietly ignored: a production
integration must never believe it is in test mode.