Skip to main content

Worked video room samples

The chain a video integration runs end to end: create a scheduled room, mint a participant access token, list the live participants once the room is open, and read the session record (recording) after the room ends. Every request carries your API key (X-API-Key) against https://api.orbit.devotel.io. Successful responses return { data, meta }; failures return Devotel Orbit’s { error, meta } shape — one handler covers both.

1. Create the room

POST /api/v1/video/rooms-scheduled
Creates a scheduled video room. name is 1–200 characters; setting scheduled_at to a future ISO 8601 time parks the room in scheduled status until first join; omit it to go live now. Gate a live or scheduled room behind recurrence, captions, recording, and a per-tier participant cap — inspect the full body in the operation list above. Request
room_id is the UUID you pass to the join (token-mint) and lifecycle calls — persist it. host_token is a LiveKit access JWT for the creator’s own first join; ws_url is the SFU WebSocket endpoint the client SDK connects to (those two are the join URLs — there is no separate url to construct). Egress ids and the storage descriptor populate only when you create the room with recording enabled; null here means recording was not started on create. If scheduled_at was in the future the room stays scheduled until the first participant joins; if it was omitted the room is live now, so composite_egress_id would carry a real id when recording_enabled: true. Cap violations return 400 VIDEO_PARTICIPANTS_CAP_EXCEEDED before any of this happens.

2. Mint the participant token

POST /api/v1/video/rooms-scheduled/{id}/join
One call per participant. identity is who the token is minted for (defaults to your own user id); role is host or participant; participant_tier (viewer / panelist / host / hidden_supervisor) overrides the role-derived grant. Minting under another user’s identity additionally requires an impersonation_reason and the owner or admin role. Request
Feed token and ws_url to a LiveKit client SDK — the JWT is not an HTTP bearer. expires_at / expires_in_seconds tell your client when to re-mint before the leg drops mid-session. participant_tier and permissions reflect the EFFECTIVE grant: an armed waiting-room lobby downgrades the token to receive-only (lobby_downgraded: true), and the participant/host role downgrade also narrows what the tier schema accepts. See guides/video-room-access-tokens for the token-claim semantics end to end.

3. Read participants and the session record

List the live roster once the room is open, then pull the ended session to read its recording.
GET /api/v1/video/rooms-scheduled/{id}
Returns room (the scheduled-room row, with settings.ingresses stripped for non-admin callers) plus the current participants list.
After the room ends, one session row per ended room carries the recording:
GET /api/v1/video/sessions/{id}
recording_url is a time-limited signed HTTPS URL ready for a <video> element or download; recording_url_expires_at is when to re-fetch the session rather than re-signing the URL yourself. A session whose recording has not finalized yet returns recording_url: null. recording_id is the unified-recordings primary key you use with the Recordings API to mint a guest share link. Poll until it appears, or listen for the video.recording.completed webhook event.

4. Errors

Failures answer { error, meta }. error carries code, status, and message; meta.request_id + timestamp mirror every request. 404 — room expired or unknown. Fetching (GET /rooms-scheduled/{id}) or minting against a room past its expiry hits NOT_FOUND before any SFU work runs. Expiry is the max_duration_minutes TTL you set on create (or the platform default on the room row), so re-create the room rather than retrying the token call.
404
429 — concurrent-room cap. A tenant’s concurrent-room cap throttles bursts of room-creation (POST /rooms-scheduled) and token-mint (POST /:id/join) calls. The two write calls are rate-limited per tenant; the fix is to drain ended rooms (list /sessions and skip live ones) before opening more, not to retry the same POST in a loop.
429
Two more envelope codes worth special-casing before the generic handler: 403 PARTICIPANT_TIER_NOT_PERMITTED (the caller’s role cannot mint the requested participant_tier) and 403 IMPERSONATION_NOT_PERMITTED / 400 IMPERSONATION_REASON_REQUIRED on the token-mint path — both mean re-minting without changing the request will fail again the same way. For the embedded-UI version of this same chain (the operator-facing Meetings dashboard that posts to these same endpoints), see guides/video-meetings. For token claims, scopes, and the lobby-downgrade semantics in depth, see guides/video-room-access-tokens.