"The client may hold a token for one socket. It never holds the key to the account."
The Relay You Don't Want
Realtime transcription has an awkward shape. The audio must stream continuously from the microphone, and the account key must never reach a browser or a phone. The obvious answer is a relay: the client streams to your server, your server holds the key and streams on to the provider. It works, and it puts every second of Dad's voice through one more hop, one more process that can stall, and one more place that sees raw audio. It also doubles your bandwidth for no benefit.
Single-Use Tokens
ElevenLabs offers a cleaner shape, and recommends it for clients: a single-use token. A server that holds the key asks the provider for a token of type realtime_scribe. The token opens exactly one realtime socket, is consumed on first use, and expires after fifteen minutes. The client connects to the provider directly with that token in the URL, so the audio passes through no relay and the key never leaves the server. Minting a token is free; the socket bills for the audio it hears.
Three Layers, Three Owners
In the family this splits across two services, and the split is the lesson:
- Bellows owns the provider. It holds the account key and exposes one route that mints a token and returns it with the socket URL and model name. That is all it knows about listening.
- cwkPippa owns the conversation's policy. Its
/api/stt/realtime-sessionroute asks Bellows for a token, then builds the complete socket URL around it: the model, the token,pcm_16000audio, thevadcommit strategy with its silence threshold, the language Dad picked, and the keyterms. It returns that URL together with the sample rate, the silence window and the token's lifetime. - The client just connects. The web page, the phone and Firekeeper on the Mac all receive the same finished URL and open it. None of them decides the silence window or the vocabulary, so none of them can disagree.
Keyterms, Derived Not Written
Scribe accepts keyterms, words it should expect and bias toward. A transcriber has never heard of "Bellows" or a soul named Ttori; without help it writes whatever common word sounds closest. The realtime session passes the display names of the souls the current soul is allowed to see, read from the soul registry at the moment the session is built. It is never a hand-written list, for two reasons. A hand list goes stale the day a new soul is born. And a hand list would leak: a soul another soul must not see stays out of every listing in the house, and the keyterm list follows the same rule because it is built by the same function.