# How Corti's speech-to-text architecture works: choosing /transcribe, /streams, or /transcripts

> Dictation, ambient encounters, or batch files? Each maps to a different endpoint with a different transport protocol and state model, not a feature toggle.

*2026-06-18* — [https://corti.ai/guides/how-cortis-speech-to-text-architecture-works](https://corti.ai/guides/how-cortis-speech-to-text-architecture-works)

## Key takeaways

- Three endpoints, three architectures: `/transcribe` (stateless WSS, dictation), `/streams` (stateful WSS, conversational), `/transcripts` (REST, async batch).
- `/transcribe` and `/streams` are both real-time WebSocket endpoints but run different speech models: single-speaker dictation versus multi-speaker conversation with diarization and fact extraction.
- `/transcripts` processes recorded audio files asynchronously via REST.

Corti's three speech-to-text endpoints share the same medical domain models and language support. Where they diverge is everything else: transport protocol, state model, speech processing pipeline. Picking the right one is an architectural decision, not a configuration toggle.

**The stateless and stateful distinction matters in practice**. A stateless connection means each WebSocket session is independent: the server keeps no interaction context between connections. A stateful connection means the server holds an interaction object for the life of the session. That is what lets `/streams` accumulate conversational context and produce structured clinical outputs over time.

![__wf_reserved_inherit](https://cdn.sanity.io/images/zrkruehe/production/47b7e181ff51359256bd5c8c101b7f2f659e8c1b-600x600.svg?w=1600&auto=format)

*Symphony for Speech to Text Overview*

## The `/transcribe` endpoint

`/transcribe` is the dictation endpoint. It combines real-time speech-to-text with command-and-control, which makes it a fit for EHR documentation, structured note capture, and any workflow where a clinician speaks to produce a document rather than hold a conversation.

Transport is WebSocket Secure (WSS) and the architecture is stateless. You open a connection, stream audio, receive transcript segments, and close the connection, with no server-side state between sessions. That suits dictation workflows, because each session is self-contained: the clinician speaks, the text is produced, the session ends.

Interim results are available on `/transcribe`. These are low-latency previews of in-progress transcript text you can surface in the UI to confirm that audio is being processed. Final results replace interim results as Symphony confirms each segment.

The command-and-control layer is what sets `/transcribe` apart from a generic STT stream. You can define commands that trigger application actions when recognized: inserting templates, navigating an EHR, automating repetitive tasks. That gives the dictation session control over the host application beyond text insertion. Map out your commands before implementation. The set that makes sense for a desktop EHR may differ from a mobile workflow, and some actions (delete range, navigate section, insert template) need to be defined at the application layer.

Read more in the [commands documentation](https://docs.corti.ai/stt/commands).

Two dictation interaction patterns are supported:

- hold-to-talk, common with handheld microphones; recording runs only while a button is held.
- toggle-to-talk, more common with wearable or desktop microphones; the microphone toggles on and stays active until you turn it off.

Your choice affects how you structure the WebSocket lifecycle and how you handle interim-to-final segment transitions in your client. For both patterns, prefer real-time audio capture over buffered approaches. Live audio lets the application surface Corti's Audio Health events, which flag low-quality audio mid-session so providers can fix it in the moment instead of discovering the problem afterward.

Read more in the [dictation implementation guide](https://docs.corti.ai/get_started/dictation).

Punctuation on `/transcribe` works two ways:

- `spokenPunctuation: true` lets users speak punctuation marks explicitly ("period", "comma")
- `automaticPunctuation: true` lets Symphony infer and insert punctuation.

Both can be enabled at the same time.

## The `/streams` endpoint

`/streams` is a stateful, bidirectional WSS endpoint built for ambient documentation and clinical decision support. Unlike `/transcribe`, which is oriented toward a single speaker dictating into a device, `/streams` is built for ambient conversational audio: a clinical encounter between a clinician and a patient, captured and transcribed in real time with speaker attribution.

The stateful architecture is fundamental to what `/streams` does. When you start a `/streams` session, you create an Interaction object on the server, which returns a WebSocket URL and an `interactionId`. The interaction persists across the session and is the container for both the live transcript and any extracted clinical facts.

The `mode.type` parameter controls what the endpoint outputs during the session. Set it to `transcription` for a live conversational transcript, `facts` for real-time clinical fact extraction via FactsR™, or both. Transcripts arrive about every three seconds; facts about every sixty seconds, though both cadences can be tuned for specific use cases.

FactsR™ is the embedded reasoning layer on `/streams`. It is a real-time agentic reasoning system for clinical consultations, designed to cut AI-generated note bloat by keeping extracted facts tightly aligned with what was actually said, rather than producing generalized summaries. In practice, showing extracted facts to clinicians before document generation has been shown to reduce provider review time, raise adoption, and lower hallucination rates in generated documentation. You can also supplement facts manually. Providers or application logic can inject additional context, such as a patient's problem list from the EHR, as discrete facts before generation to ensure completeness even when a topic never comes up in the consultation. For developers building ambient documentation pipelines, this is the primary integration point for producing structured clinical output from live audio.

Read more about FactsR™ in [our docs](https://docs.corti.ai/textgen/factsr).

Diarization is available on `/streams` and is configured at session initiation. Setting `isDiarization: true` enables speaker separation on single-channel audio. For multi-channel audio, where each speaker is captured on a separate channel, `isMultichannel: true` pairs with a `participants` array that assigns roles (clinician, patient) to each channel. Multi-channel diarization is generally more accurate than single-channel, because the speaker separation happens at the hardware level rather than being inferred from the audio signal.

As with `/transcribe`, prefer real-time audio streaming via `/streams` over post-encounter upload where connectivity permits. Live capture enables Audio Health events, which surface audio quality issues during the encounter rather than after, giving applications the chance to prompt providers to adjust before the session is lost.

## The `/transcripts` endpoint

`/transcripts` is the batch processing endpoint. It accepts uploaded audio files via REST and produces transcripts asynchronously, which makes it the right choice for post-call transcription workflows: recorded consultations, archived telehealth sessions, or any scenario where the audio already exists and does not need real-time processing.

The `/transcripts` workflow takes three sequential API calls. Create an Interaction to get an `interactionId`, upload the audio file via `POST /interactions/{id}/recordings/` to get a `recordingId`, then submit the transcript request via `POST /interactions/{id}/transcripts/` referencing both IDs. One interaction can hold more than one audio file and transcript.

![__wf_reserved_inherit](https://cdn.sanity.io/images/zrkruehe/production/657f1cb127af46a4d3e5b1c7c8874f414668e933-600x600.svg?w=1600&auto=format)

*Structured transcripts after using live or batch endpoints with Corti Symphony*

## Choosing between endpoints

The decision comes down to three questions. Is the audio being produced now, or was it recorded previously? Is one speaker dictating, or are multiple speakers in conversation? Does the application need to react to the transcript in real time?

If the audio is live and the use case is single-speaker dictation with application control, use `/transcribe`. If the audio is live and the use case is a multi-speaker clinical encounter with ambient note generation or decision support, use `/streams`. If the audio is a recorded file and latency is not a constraint, use `/transcripts`. These are general recommendations; assess your solution carefully before committing.

Many clinical applications need both `/transcribe` and `/streams`: dictation for provider-driven documentation, and ambient streaming for encounter-level capture. Designing the UX so providers understand when to use each mode matters as much as the technical integration. The [dictation](https://docs.corti.ai/get_started/dictation) and [ambient scribe](https://docs.corti.ai/get_started/ambient-scribe) implementation guides cover both workflows in depth.

For any of the three endpoints, the Interaction object is the shared abstraction. It is the container that links audio, transcripts, and any downstream clinical outputs, and its `interactionId` is the key that runs through the entire processing chain.

For full API specifications, configuration parameters, and code examples, see the [Corti API documentation](https://docs.corti.ai/).
