Beta. Live transcription is available for testing in the Python and TypeScript SDKs. Event shapes, configuration options, and error semantics may change in backwards-incompatible ways before GA. Pin to the SDK versions you test against.
Overview
Stream raw audio bytes to CAMB over a single WebSocket and receive cumulative transcripts in real time. The session exposes a typed event dispatcher (Ready, Results, Error, Closed), a built-in microphone helper, and forward-compatible onAny subscription for events the server may add in future releases.
Key features:
- Interim and final results β interim
Results(is_final: false) carry the cumulative transcript so far for the current utterance; render them as a live preview. A finalResults(is_final: true) closes the utterance; commit it so each utterance is preserved instead of being overwritten by the next one. - Word-level timing β the final
Results(is_final: true) includes per-word start/end timestamps and confidence. Interim frames carry an emptywordsarray. - Typed events β the same typed event surface in both SDKs, with per-event typed payloads.
- Easy extensibility β new server event = one enum entry + one payload type + one parser entry. Nothing else changes.
- Microphone helpers β
sounddevicein Python,AudioWorkletin the browser,node-record-lpcm16in Node.
How segments and cumulative updates work
Speech arrives as a series of utterances. Within one utterance the server streams cumulative interimResults (is_final: false) β each adds whole words to the transcript so far (the model emits complete words, never partial-word fragments). After a short pause in the audio the server finalizes the utterance with a Results whose is_final is true, carrying the complete utterance; the next Results then starts a brand-new utterance from an empty string.
R* marks the final frame. Because the transcript resets on every new utterance, replacing your UI with the latest transcript and nothing else makes each utterance erase the previous one β the bug you see as text βrewriting the same lineβ after a pause. Instead, show the interim frames as a live preview, then commit the text when is_final is true (print it on its own line, or append it to a list) so finished utterances are preserved. When the client stops sending audio, the connection closes cleanly with WebSocket close code 1000.
Live Transcription SDK vs Async Transcription SDK
Prerequisites
1
Create an account
Sign up at CAMB.AI Studio if you havenβt already.
2
Get your API key
Go to Settings β API Keys in Studio and copy your key. See Authentication for details.
3
Install the SDK
4
Set your API key to use in your code
Get Started
Create an API Key
Generate a key at CAMB.AI Studio and export it asCAMB_API_KEY for the snippets below.
Install
sounddevice as a regular dependency, so the Microphone helper works out of the box. In Node the microphone adapter additionally requires the host sox binary. The browser adapter needs no extra packages β it uses getUserMedia and an inlined AudioWorklet.
Quickstart
Events and Payloads
Supported events
Both SDKs expose the typed events below through a singleServerMessageType enum. Source tells you who emits each one. UtteranceEnd is a raw wire event with no dedicated enum member β it arrives through the onAny catch-all.
Catch-all subscription. If a future server release adds a new event type before the SDK does, the dispatcher still delivers it to any handler registered via
session.on_any(...) (Python) / session.onAny(...) (TypeScript) with the raw payload. Applications stay forward-compatible without forking the SDK.
How events work
The session reads JSON frames off the WebSocket, looks up the wiretype in a parser registry, builds the typed payload, and fans out to every handler registered for that event. Unknown event types are still delivered through onAny so applications keep working when the server adds new messages.
Event payloads
Results (is_final: true) carries the same fields as an interim one, with one difference: per-word timing is only populated on the final frame. Interim frames carry an empty words array ("words": []); the final frame fills in each wordβs start, end, and confidence. There is no separate Final frame on the wire β finals arrive through the same Results handler β so branch on msg.is_final (Python) / msg.isFinal (TypeScript) to decide when to read word timing and commit an utterance.
Subscribing to events
Adding a custom event
If you fork the SDK or wrap it for an internal use-case, adding a new server event is a three-step change in either language:- Add a new member to
ServerMessageType. - Define the payload (a Pydantic model in Python, an interface in TypeScript).
- Register a parser in
PARSER_REGISTRY.
Basic Configuration
Every option below is optional. Omit any to inherit the server default documented inapi-reference/websockets/asyncapi.json.
Basic configuration example
Advanced Configuration
KeepAlive
Some intermediaries (load balancers, browser proxies) close idle WebSocket connections after a few seconds of silence. If your audio pipeline can be bursty, send aKeepAlive frame between bursts.
CloseStream
session.close() (Python: same name) sends {"type": "CloseStream"} and waits for the serverβs clean 1000 close. Always prefer this over just hanging up β it ensures the server flushes any pending transcript.
Bring-your-own transport
The Python SDKβsconnect() accepts a transport argument implementing the Transport protocol. The TypeScript client accepts a transport: () => Transport factory. Use this to inject a mock during testing or to plug in a custom WebSocket implementation.
Microphone Helpers
Python β sounddevice
sounddevice ships with camb-sdk, so no extra install step is needed. On Linux you may need to install PortAudio system libraries (e.g. apt install libportaudio2) β sounddeviceβs docs cover platform prerequisites.
TypeScript β browser
getUserMedia, then downsamples to the requested rate inside an AudioWorklet so the server always sees PCM16 LE little-endian.
TypeScript β Node
node-record-lpcm16, declared in package.json as an optionalDependencies entry. The host machine also needs the sox binary on PATH.
Error Handling and Close Codes
Server errors
Whenever the server cannot continue, it emits anError frame and closes with a non-1000 code:
Transport errors
Connection-level failures (DNS, TLS, mid-stream drops) are surfaced through the sameError event with code: "transport_error" (TypeScript) or code: "handler_exception" (Python), keeping a single observable channel for application code.
Close codes
Timeout
The Live Transcription API has an internal timeout of 1 hour. Please add retries to handle/create further connections.More Information
/transcription/listenWebSocket reference β the underlying wire protocol.- Python SDK Β· TypeScript SDK β the full SDK guides.
- Source:
cambai-python-sdkΒ·cambai-typescript-sdk.