Skip to main content

Worked room lifecycle: create → token → record → fetch

The run-through every video integration does first: create a room, mint a join token for a participant, record the session, then read the session’s signed playback URL once the room has ended. All requests use your API key (X-API-Key) against https://api.orbit.devotel.io.

1. Create the room

POST /api/v1/video/rooms
Room names must be URL-safe (letters, digits, _, -, 3–200 characters). The response returns the SFU room name plus a host token and the WebSocket URL the host client connects to. Request
room_name carries a server-side tenant prefix; display_name is the label you supplied. Feed the returned token and url to a LiveKit client SDK to pick up audio and video — token is a LiveKit access JWT, not an HTTP bearer token. The scheduled-room surface (recurrence, registration, simulive, webinar layouts) is a sibling surface under POST /api/v1/video/rooms-scheduled — its create call also mints the orbit-media egress when recording_enabled: true, so skip step 3 for those rooms.

2. Mint a participant access token

POST /api/v1/video/rooms/{name}/token
One call per participant. participant_name is the identity stamped on the token; can_publish defaults to false (receive-only) and only an owner or admin caller can set it to true. Minting a token under someone else’s identity additionally requires impersonation_reason and an owner or admin role, or the request is rejected with a four-hundred status. Request
participant_tier is the finer-grained control: viewer (receive-only), panelist (publishes audio/video), host, and hidden_supervisor. When supplied it overrides can_publish; host and hidden_supervisor require the owner or admin role. When omitted, the legacy can_publish boolean controls the grant exactly as before.

3. Record the session

Recording is started by the host from inside the room (or automatically at room creation for scheduled rooms created with recording_enabled: true), and completed recordings are attached to the room’s session history by the recording webhook. There is no second API call to make here — once the room ends, the recording finalizes asynchronously. Listen for the video.recording.completed webhook event (see Webhook events) or poll GET /api/v1/video/sessions until the session row appears with a non-null recording_url.

4. Fetch the recording asset

GET /api/v1/video/sessions/{id}
Each ended room produces one session row. Fetch it by id — the response carries recording_url as a time-limited signed HTTPS URL ready for a <video> element or download, plus recording_url_expires_at so you know when to re-fetch. A session whose recording has not finalized yet returns recording_url: null.
recording_id is the unified-recordings primary key — use it with the Recordings API to mint a time-limited guest share link for a non-account viewer, or to pull the stored transcript. Treat both recording_url and its signature as opaque and re-fetch the session when recording_url_expires_at passes rather than re-signing it yourself.

Response envelopes

Every endpoint on this page answers in one of two envelope shapes, so one error handler covers all four steps above. Successdata carries the payload (a single object, or an array on list endpoints such as GET /api/v1/video/sessions), and meta carries request_id plus timestamp. List endpoints add a pagination block under meta:
While meta.pagination.has_more is true, pass meta.pagination.cursor back as the cursor query parameter to fetch the next page — the cursor is opaque; keep the same filters across pages and stop when has_more comes back false. Error — failures answer with { error, meta }, where error carries code, status, and message. Validation failures return 422; token privilege violations return 403 in this same shape:
403
Handle PARTICIPANT_TIER_NOT_PERMITTED and IMPERSONATION_NOT_PERMITTED before the generic error path — both mean the caller’s role cannot mint the requested token, and retrying without changing the request will fail the same way.