Skip to main content

Stream Video Room audio to your WebSocket

Use a media fork to send live audio from a scheduled Video Room to a WebSocket server that you operate. This guide walks through starting a fork, receiving its frames, and stopping it safely. Before you start, create a scheduled room and have its UUID and a participant identity ready. Your receiver must be reachable at a public wss:// URL on port 443. Keep your API key and WebSocket credentials in a secret store, not in source control.

What a media fork does

A media fork is the Video Rooms counterpart of the voice listen verb: Orbit starts the connection to your wss:// endpoint and pushes live room audio to it. Orbit joins the room as a hidden, listen-only participant; it does not publish media. Media forks support audio only. Video is not available through this endpoint. Omit media or set it to "audio"; any other media value returns 400 MEDIA_FORK_AUDIO_ONLY.

Authentication and roles

Call the start and stop endpoints with an API key in X-API-Key. The caller must have the owner or admin role and the video:write scope. The start and stop actions are recorded as video_room.media_fork_started and video_room.media_fork_stopped audit events in your workspace. wsAuth is separate from API authentication. When supplied, Orbit sends your username and password as HTTP Basic authentication on its WebSocket handshake to your receiver. Use distinct credentials for this endpoint and validate them before accepting the WebSocket upgrade. If your workspace has recording-consent enforcement enabled, include a valid recording_consent_receipt_id when starting the fork. The setting and receipt are controlled by your workspace; without a required receipt, the API returns 422 RECORDING_CONSENT_REQUIRED.

Start a media fork

Send POST /api/v1/video/rooms-scheduled/{id}/media-fork. Set participant_id to one participant’s current room identity to capture that participant, or set it to "all" to capture everyone. An "all" fork also includes participants who join later, but it can start only when at least one participant is already in the room. mixType defaults to "mono". For a single-participant target, you can choose "stereo": the chosen participant is on channel 0 and the other participants are mixed on channel 1. stereo with participant_id: "all" is rejected by request validation. sampleRate defaults to 16000 and accepts 8000, 16000, 24000, or 48000 Hz. metadata is an optional JSON object of up to 4 KB; Orbit returns it in the first WebSocket message. The endpoint URL must use wss://, port 443, and resolve to a public address. Local, private, and internal addresses are rejected. The API accepts the request with 202 and returns a fork_id; audio begins after Orbit connects to your receiver and joins the room.
For a room-wide mono stream, change the request body to "participant_id": "all" and omit mixType (or set it to "mono"). Do not combine "all" with "stereo".

Receive and authenticate frames with Node.js

The following example uses the ws package and Node’s built-in HTTPS server. Configure a trusted TLS certificate for your public hostname and set FORK_USERNAME and FORK_PASSWORD to the same values you send in wsAuth. The upgrade handler checks Basic credentials before passing the connection to the WebSocket server.
The example accepts multiple connections, including Orbit’s one reconnect, and writes only binary audio frames to stdout; control messages go to stderr. In production, pipe stdout to a downstream consumer that handles backpressure and processes frames promptly.

Wire format

Each connection starts with a text JSON message describing the stream and echoing your custom metadata. Binary messages follow, each containing one 20 ms frame of signed 16-bit little-endian PCM (L16). A final JSON text message arrives before Orbit closes the socket when the fork stops.
At 16 kHz, a mono frame is 640 bytes and a stereo frame is 1,280 bytes. Stereo samples are interleaved left and right. Orbit sends frames during silence too; silent frames contain zeros. Use sampleRate, channels, and encoding from the metadata message to configure your consumer rather than assuming the example values. When the fork stops, the last text message is shaped like this:

Lifecycle and stop conditions

Stop a fork explicitly with DELETE /api/v1/video/rooms-scheduled/{id}/media-fork/{forkId}, using the room UUID and the fork_id returned by the start request. A successful response reports stopped if it has ended, or stopping while shutdown completes; in the latter case, wait for the final stopped WebSocket message. Orbit also stops a fork when the room ends, when the selected participant leaves, or when an "all" fork’s room has been empty for 60 seconds. If your receiver loses the customer-side socket, Orbit attempts one reconnect and sends the metadata message again; if it cannot reconnect, the fork ends. Each fork is capped at four hours. The final message’s reason can be stopped, room_disconnected, participant_left, room_empty, max_duration, audio_stream_failed, or shutdown. Start a new fork after an automatic stop if you still need the stream.

Limits and errors

You can run up to 5 active forks in one room and 20 in one tenant. Exceeding either cap returns 429 MEDIA_FORK_LIMIT_REACHED. These are tenant-level usage limits; manage concurrent forks within your workspace. Send frames to a live speech-to-text service, analyze talk time or audio quality, or feed a real-time analytics pipeline. Your receiver controls what it stores and processes; apply your workspace’s consent and retention settings to that data. For the voice-call equivalent, see the listen verb reference. To manage scheduled Video Rooms and their participants, see Video Room consoles. For the full endpoint contract, see Audio media fork in the Video API reference.