APISDKDocs
transcription: ask for diarization, word timings, language hints and detection
All three transcription routes now take the same five options -- diarize, word_timestamps, smart_format, detect_language and language_hints -- and route only to providers that can honour them. A request no available provider can serve is refused rather than answered without the feature. Streaming config frames now reject unknown fields with INVALID_CONFIG instead of ignoring them in silence.
- POST /v1/transcribe and POST /v1/audio/transcriptions accept diarize (diarization is accepted too), word_timestamps, smart_format, detect_language and language_hints as query parameters; language_hints is comma-separated, at most eight tags, and every tag must be a language this router is configured to serve. GET /v1/transcribe/stream accepts the same five in its config frame as diarize, wordTimestamps, smartFormat, detectLanguage and languageHints, each also accepted in snake_case. Query keys the router does not own keep reaching the selected provider exactly as before.
- The options narrow the candidate set and fail closed. When no available provider can serve one on the transport in use, a buffered request answers 422 unsupported_feature naming the feature and the transport, and the socket answers an UNSUPPORTED_FEATURE error frame and closes; neither spends an upstream call. A transcript with no speakers in it is indistinguishable from a recording of one speaker, so a request that was accepted and quietly not honoured would never be reported. smart_format is the exception and narrows nothing.
- What a provider can do depends on the transport, and the router now records that per transport rather than per provider. AssemblyAI labels speakers and times words on a buffered upload and its socket carries neither; ElevenLabs is the same; Deepgram's language detection is a pre-recorded feature; Soniox is the only provider that accepts several candidate languages, on either transport, and its hint list finally carries all of them instead of the one language the request resolved to. Diarization on OpenAI transcription remains unavailable: OpenAI diarizes only through a model that is not on the routing board.
- POST /v1/audio/transcriptions also honours OpenAI's own two parameters. timestamp_granularities[] containing word means the same as word_timestamps=true, with the query parameter winning when both are sent. response_format accepts json (the default) and verbose_json, which both answer the same transcription object, and text, which answers text/plain with the transcript alone; srt and vtt answer 400 unsupported_response_format because both are built from segment timings the answering provider may supply none of. This also fixes response_format=text and response_format=verbose_json, which were forwarded to the provider verbatim and failed the request with 502 after trying every candidate.
- GET /v1/routing/preview accepts transport=buffered|streaming (buffered by default) and the five options, applying the same narrowing. A streaming preview reports the socket's own dial order and verified-language set, so a Russian streaming preview now reports soniox:stt-rt-v5 where the buffered preview reports OpenAI -- the disagreement disclosed when Russian streaming shipped. A streaming preview only names configured providers, so routable_only=false cannot widen it.
- @speko/web takes diarize, wordTimestamps, smartFormat, detectLanguage and languageHints on connectTranscription and sends only the options that were set.
Action: One behavior change needs attention: a GET /v1/transcribe/stream config frame carrying a field the router does not define, including inside sttOptions, is now refused with the existing {"type":"error","code":"INVALID_CONFIG"} frame and the socket closes. Such a frame was previously accepted and the unknown field ignored, so a misspelled option produced a working socket that did none of what was asked. Send only type, language, interimResults, sampleRate, sttOptions.language and the five options above; both plugins and @speko/web already do. Nothing else requires migration: every option is opt-in, transcripts for callers that send none are unchanged, and a caller that sets one should expect 422 unsupported_feature or an UNSUPPORTED_FEATURE frame when their allow list leaves no provider that can serve it.