Transcription
Transcription is Cortex’s core capability. A headless bot joins an ODIN Voice room, receives a separate audio stream for each speaker, detects when someone is talking, transcribes each utterance, and stores it as a message — attributed to the right person, in real time.
How it works
The key detail: the bot receives individual per-peer audio, not a mixed stream. That’s what makes attribution accurate — every message is tied to the participant who spoke it.
Which provider turns the audio into text is up to you: by default Cortex transcribes with OpenAI on 4Players’ account, or you can bring your own key or endpoint.
Cortex runs and scales this bot for you. If you'd rather build and host your own, the ODIN Voice Node.js SDK gives you the same per-speaker audio access Cortex relies on — see the Transcribing Audio sample. Cortex is the managed version of that approach.
Sessions and messages
- A session is the active transcription of one room. Create it with the
externalRoomIdof the ODIN room you want to transcribe — the bot joins right away. See Sessions & Gatherings. - Each transcribed utterance becomes a message on that session, attributed to a participant.
Reading the transcript
Every message carries a per-session sequence number, seq (1, 2, 3, … without gaps). Use it
as a cursor to read only what’s new:
To process messages as they happen rather than polling, use
watchMessages() or subscribe to the message.created
event. The event carries the same seq, so after a reconnect
you can catch up with listMessages({ after: lastSeenSeq }).
Transcription providers
Speech-to-text runs through transcription plugins — a special kind of plugin that works on audio inside the bot. Each project has exactly one transcriber:
| Plugin | Who runs the speech-to-text | Credentials | Billing |
|---|---|---|---|
cortex-transcription (default) | OpenAI, operated by 4Players | None — 4Players’ account | Billed by 4Players per audio minute |
openai-transcription | OpenAI, or any OpenAI-compatible speech-to-text API | Your own API key, optional base URL | Not billed by 4Players — your provider bills you |
Every project starts on cortex-transcription — nothing to set up. Switch to
openai-transcription when you want to:
- use your own OpenAI account (your contract, your rate limits, your invoice),
- use another OpenAI-compatible provider such as Groq, or
- run speech-to-text yourself — a self-hosted, OpenAI-compatible server (for example one based on faster-whisper) keeps transcription on infrastructure you control.
Bring your own key (or endpoint)
Switching takes three steps: install the plugin with your settings, check the connection, and select the instance as the project’s transcriber.
The same selection is available over REST:
PATCH /api/projects/{projectId}/settings/transcription with
{ "transcriberInstanceId": "<instance id>" } (or null). The current choice is the
transcriberInstanceId field of the project settings (project.settings.get()).
Settings of openai-transcription:
| Setting | Required | Notes |
|---|---|---|
apiKey | Yes | Your provider’s API key. Encrypted at rest and returned masked (********). |
baseUrl | No | Empty for OpenAI. Otherwise the https base URL of an OpenAI-compatible API, e.g. https://api.groq.com/openai/v1. |
model | No | Default whisper-1. Any transcription model your provider offers (e.g. gpt-4o-transcribe, whisper-large-v3). |
language | No | ISO 639-1 code of the room language (e.g. de). Improves accuracy and latency; empty = auto-detect. |
prompt | No | Names and game terms the model should recognize. |
Good to know:
- Running sessions switch within about 30 seconds — no need to restart them.
- No silent fallback. If your provider fails (wrong key, quota exhausted, endpoint
down), Cortex does not fall back to the 4Players-billed default — that would bill you
and send your audio somewhere you didn’t choose. Those utterances are not transcribed.
Test with the
healthendpoint before selecting an instance, and watch your provider’s dashboard. - The selected transcriber is protected. While an instance is the project’s
transcriber, it can’t be disabled or deleted (
409) — select another one (ornull) first. baseUrlmust be a publichttpsURL. Private, loopback and link-local addresses are rejected, both when you save the settings and when the bot connects, and redirects are not followed. A self-hosted server therefore needs a publichttpsendpoint.- Audio never becomes an event. Raw audio stays inside the bot and goes only to the provider you selected — it is never published to webhooks, SSE or functions.
- Only the default is billed by 4Players. Minutes transcribed with your own key or endpoint are not billed by 4Players — only by your provider, if at all.
cortex-transcription and openai-transcription are available today. Further provider
types are planned.
cortex-transcription has settings as well — model (whisper-1, gpt-4o-transcribe
or gpt-4o-mini-transcribe), language and prompt. To change them, install it as a plugin
instance with your settings and select that instance. All models are billed per audio
minute.
ODIN 1.x and 2.x rooms
ODIN Voice 1.x and 2.x are not interoperable — they use different protocols and server infrastructure. Tell Cortex which major your clients use so the bot joins with a matching SDK:
Make sure the session’s gateway belongs to the same major. On ODIN 2.x rooms the bot also gets noise suppression before transcription and flushes an utterance as soon as the speaker stops talking, which lowers transcription latency.
Recording announcements
The bot speaks a short notice into the room when it joins, and again just before it leaves, so everyone knows they are being recorded. In many jurisdictions (including the whole EU) recording a conversation without informing every participant is unlawful, so announcements are on by default. You can change the wording — in any language — or switch them off if your application obtains consent another way:
Texts are limited to 500 characters and can’t be blank (use enabled: false to turn
announcements off). Changes apply to the next session.
End-to-end encrypted rooms
If your rooms use ODIN end-to-end encryption, the bot needs the same cipher password as your clients. Choose how it gets it:
| Mode | Bot behavior |
|---|---|
disabled (default) | Joins without a cipher — for unencrypted rooms. |
automatic | Uses the room’s externalRoomId as the cipher password — your clients must do the same. |
manual | Uses a fixed password you configure. |
Summaries
A session can produce an AI summary of the conversation (recap, action items, a shareable transcript) through the Session Summary plugin. You can regenerate it on demand:
Summaries arrive as session annotations and the
session.annotation.created event — handy for emailing a recap when a call ends (see the
serverless function guide).
Debug audio (optional)
For troubleshooting, a project can enable debug audio, which retains the exact audio that was sent to the transcription provider for each message for 24 hours, so you can play back what was heard. It’s off by default because it stores voice data.
Transcripts — and especially debug audio — can contain personal data. Enable debug audio only when you need it, and make sure your privacy notices cover transcription.
Identifying speakers
The bot maps each audio stream to a participant using the externalUserId your client
passed to ODIN when joining the room. Make sure your clients pass a stable, meaningful
userId so transcripts attribute correctly. See
Users & Participants.
Players with an active voice mute can also be excluded from transcription on ODIN 2.x rooms — see Moderation.
Related
- Plugins
- Getting Started
- Real-time updates
- Moderation
- Room Push — live captions in the room
- TypeScript SDK