TTS model list API: read the catalog before you hardcode a Sonic id
Sume's TTS Router lists its models at GET /v1/tts-router/models. Read it, pin sonic-3.6, and treat sonic-preview as a beta channel that can change.

If you want to choose a text-to-speech model by id through Sume, call GET /v1/tts-router/models and use what it returns. The router is a pass-through for Cartesia Sonic only: sonic-3.6, sonic-3.5, sonic-3, sonic-latest and sonic-preview. An unknown id fails with 400 model_not_found and a pointer to the catalog URL, so a typo fails fast.
Two routes, one contract
TTS 1.0 is the managed surface and has no engine picker, and sending model or model_id to it is a 400. It always uses sonic-3.6, the current stable Sonic release at the provider. Use POST /v1/tts-router/generate only when you need a specific id. Everything else is shared: the same transcript rules, voice selectors, language, output_format, timestamps, and webhook fields.
Which id to pin
Pin sonic-3.6 when output must stay stable. sonic-latest is an alias that resolves to sonic-3.6 today, so a pin by alias can move when the provider ships a newer release. sonic-preview is a beta channel, its output and availability can change without notice, and it rejects pro voice clones with voice_model_mismatch.
curl -s https://api.sume.com/v1/tts-router/models \
-H "Authorization: Bearer $SUME_API_KEY"
curl -X POST https://api.sume.com/v1/tts-router/generate \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: router-pin-001" \
-d '{"model": "sonic-3.6", "transcript": "Pinned to one model.", "voice": {"id": "'"$VOICE_ID"'"}}'How each id behaves, from the Sume router docs (read 2026-10-06):
| Model id | Behaviour | Use it for |
|---|---|---|
| sonic-3.6 | Current stable Sonic release | Production narration |
| sonic-3.5 | Earlier release | Matching older audio |
| sonic-3 | Earlier release | Matching older audio |
| sonic-latest | Alias that resolves to sonic-3.6 | Tracking the newest stable |
| sonic-preview | Beta channel, may change | Testing only |
A safe integration pattern
Fetch the catalog at startup and cache it for a short time. Validate the id you plan to send against the list, and fall back to TTS 1.0 if it is missing. That way a retired id cannot break a nightly batch.
Log the job.model of every result. The docs say job.model echoes the id you requested, so the log shows which model id made each file. If a client later says a voice sounds different, you can see whether the model id changed or only the text.
Each catalog row is metered by character with the provider list price times 1.25, the same book as TTS 1.0. The catalog response shows the price per model, so read it from there and not from a copy in your code. Later vendors would arrive as new rows in the same list, not as a new surface.
Sources
Related posts
More in Developers
- tts_sentence_selection_invalid 422 on Sume TTS: what triggers it
Sume TTS returns 422 tts_sentence_selection_invalid for gaps, repeated jobs, unfinished jobs and partial coverage. Each cause and its fix.
- tts_source_integrity_mismatch 422: job differs from accepted script
verify-spine returns 422 tts_source_integrity_mismatch when a finished TTS job's text no longer matches the accepted script. What it checks and how to recover.
- tts_source_not_found 404 on Sume TTS: revision, sentence or job
A 404 tts_source_not_found from the Sume script-source API means the revision, a sentence id or a selected job is not visible to this key or thread.
- tts_source_revision_mismatch 409: stale expected_script_revision_id
Sume returns 409 tts_source_revision_mismatch when the accepted script changed under you or a run is frozen. How to re-read the revision and retry.
Written by Sume