Skip to main content

Documentation Index

Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt

Use this file to discover all available pages before exploring further.

Video API

Real-time video rooms backed by Orbit Media (Devotel’s hosted SFU; forked from LiveKit OSS under Apache-2 License — see attribution). Two flavors:
  • Scheduled rooms (/api/v1/video/rooms-scheduled) — DB-backed, first-class rooms with a host, schedule, capacity, recording, and per-room guest invites. Use when you want the room to outlive a single session, surface in the inbox as a video conversation, or hand out shareable join links.
  • Ad-hoc rooms (/api/v1/video/rooms) — fire-and-forget Orbit Media rooms created directly against the Orbit Media room service. Use for “click to start a call” flows where the room ends when participants leave.
Base path: /api/v1/video Authentication: API key (X-API-Key) on every authenticated endpoint. The public guest-redeem endpoint (POST /video/invites/:inviteToken/redeem) takes the invite token itself as the credential and requires no API key. Role requirements: mutating endpoints (create, delete, end, recording start/stop, invite create/revoke, participant kick/mute) require owner, admin, or developer. Read endpoints (list, get) and POST /rooms-scheduled/:id/join are available to any authenticated tenant member; the join route silently downgrades role: "host" to participant for callers that lack the host-mint role. Orbit Media configuration: every endpoint returns 503 SERVICE_UNAVAILABLE if DEVOTEL_ORBIT_MEDIA_URL / DEVOTEL_ORBIT_MEDIA_API_KEY are not set on the cluster. (Legacy DEVOTEL_LIVEKIT_* env vars remain as deprecated fallbacks and will be removed in a future release; prefer DEVOTEL_ORBIT_MEDIA_* for all new deployments.) Wire compatibility: Orbit Media speaks the same JWT-signed signaling protocol as the LiveKit OSS fork it descends from. The published @orbit/media-client browser SDK is the supported client. The upstream livekit-client SDK still works at the protocol level today but is not part of Orbit’s supported surface and may diverge.

Scheduled rooms

First-class video rooms backed by tenant_<id>.video_rooms. Each room creates a companion conversations row (channel='video') so the inbox renders it alongside other channels.
Single-occurrence only. Each scheduled room represents one meeting instance — there is no rrule / recurrence / series field today, and the generated .ics calendar attachment does NOT carry an RRULE line. For a weekly stand-up, daily check-in, or any other recurring cadence, the caller must create one scheduled room per occurrence. Native RFC 5545 RRULE support (FREQ=DAILY/WEEKLY/MONTHLY with BYDAY, INTERVAL, COUNT, UNTIL) is on the roadmap; the underlying ICS generator already supports it, but the meeting-create surface intentionally does not expose it until per-occurrence overrides (EXDATE, single-instance reschedule) and the cancellation/re-send flow for series updates land.

Schedule a Room

POST /api/v1/video/rooms-scheduled Create a new scheduled video room (single occurrence — see the “Single-occurrence only” note above). Tenant-isolated. Optionally pre-fills a start time, enables auto-recording, and caps capacity. Returns a host token immediately so the creator can join without a second round-trip.
name
string
required
Human-readable room title (1–200 chars). Surfaces in the inbox and join UIs.
scheduled_at
string
ISO-8601 timestamp with offset (e.g. 2026-05-10T14:00:00Z). Omit or null for “start now”. Single occurrence only — no rrule companion field today.
recording_enabled
boolean
default:"false"
When true, recording starts automatically the moment the first participant joins.
max_participants
integer
default:"10"
Hard cap on concurrent participants (1–500). Orbit Media rejects join attempts past this number.
settings
object
Arbitrary key-value pairs persisted on the room row. Surfaces back on GET /:id.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Onboarding call - Acme",
  "scheduled_at": "2026-05-10T14:00:00Z",
  "recording_enabled": true,
  "max_participants": 4
}'
Node.js
import { Orbit } from '@devotel/orbit-sdk'

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY! })

const room = await orbit.video.roomsScheduled.create({
  name: 'Onboarding call - Acme',
  scheduled_at: '2026-05-10T14:00:00Z',
  recording_enabled: true,
  max_participants: 4,
})
console.log(room.data.room_id, room.data.host_token)
Python
import os, requests

headers = {"X-API-Key": os.environ["ORBIT_API_KEY"], "Content-Type": "application/json"}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled", headers=headers, json={
  "name": "Onboarding call - Acme",
  "scheduled_at": "2026-05-10T14:00:00Z",
  "recording_enabled": True,
  "max_participants": 4
})
print(r.json())
Go
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled", bytes.NewBuffer([]byte(`{
  "name": "Onboarding call - Acme",
  "scheduled_at": "2026-05-10T14:00:00Z",
  "recording_enabled": true,
  "max_participants": 4
}`)))
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: ' . getenv('ORBIT_API_KEY'),
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, <<<JSON
{
  "name": "Onboarding call - Acme",
  "scheduled_at": "2026-05-10T14:00:00Z",
  "recording_enabled": true,
  "max_participants": 4
}
JSON);
echo curl_exec($ch);
{
  "data": {
    "room_id": "8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002",
    "room_sid": "RM_AbCdEfGhIjKl",
    "host_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "ws_url": "wss://media.orbit.devotel.io"
  },
  "meta": {
    "request_id": "req_video_001",
    "timestamp": "2026-05-10T12:00:00Z"
  }
}
Error codes: 503 SERVICE_UNAVAILABLE (Orbit Media not configured), 400 (validation), 403 (caller lacks owner/admin/developer), 500 VIDEO_ROOM_CREATION_FAILED.

List Scheduled Rooms

GET /api/v1/video/rooms-scheduled Retrieve rooms scheduled by the current tenant. Filter by lifecycle status.
status
string
Filter by lifecycle: scheduled, live, ended. Omit for all statuses.
limit
integer
default:"100"
Results to return (1–500).
cURL
curl -X GET "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled?status=live&limit=50" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
const rooms = await orbit.video.roomsScheduled.list({ status: 'live', limit: 50 })
console.log(rooms.data)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled",
                 headers=headers, params={"status": "live", "limit": 50})
print(r.json())
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled?status=live&limit=50", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled?status=live&limit=50');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
{
  "data": [
    {
      "id": "8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002",
      "name": "Onboarding call - Acme",
      "room_sid": "RM_AbCdEfGhIjKl",
      "host_user_id": "user_2x7Q...",
      "status": "live",
      "scheduled_at": "2026-05-10T14:00:00Z",
      "started_at": "2026-05-10T14:00:12Z",
      "ended_at": null,
      "recording_enabled": true,
      "recording_url": null,
      "max_participants": 4,
      "settings": { "conversation_id": "conversation_kPdE..." },
      "created_at": "2026-05-09T22:14:00Z",
      "updated_at": "2026-05-10T14:00:12Z"
    }
  ],
  "meta": { "request_id": "req_video_002", "timestamp": "2026-05-10T14:01:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (invalid query), 500 VIDEO_ROOM_LIST_FAILED.

Get a Scheduled Room

GET /api/v1/video/rooms-scheduled/{id} Fetch a single room plus the live participant roster.
id
string
required
Room UUID returned from POST /rooms-scheduled.
cURL
curl -X GET "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
const detail = await orbit.video.roomsScheduled.get('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002')
console.log(detail.data.room, detail.data.participants)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002", headers=headers)
print(r.json())
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
{
  "data": {
    "room": {
      "id": "8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002",
      "name": "Onboarding call - Acme",
      "room_sid": "RM_AbCdEfGhIjKl",
      "status": "live",
      "scheduled_at": "2026-05-10T14:00:00Z",
      "started_at": "2026-05-10T14:00:12Z",
      "ended_at": null,
      "recording_enabled": true,
      "max_participants": 4
    },
    "participants": [
      {
        "identity": "user_2x7Q...",
        "display_name": "Jane Doe",
        "role": "host",
        "joined_at": "2026-05-10T14:00:12Z",
        "left_at": null
      }
    ]
  },
  "meta": { "request_id": "req_video_003", "timestamp": "2026-05-10T14:02:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (id not a UUID), 404 NOT_FOUND, 500 VIDEO_ROOM_FETCH_FAILED.

Join a Scheduled Room

POST /api/v1/video/rooms-scheduled/{id}/join Generate a per-participant Orbit Media access token. The resulting JWT is short-lived and scoped to one room + identity.
identity
string
required
Unique participant identity (1–200 chars). Used as the Orbit Media participant id. Reusing the same identity in the same room kicks the prior session.
display_name
string
Friendly name shown to other participants.
role
string
default:"participant"
participant or host. Server-side guard: callers that are not owner / admin / developer are silently downgraded to participant.
user_id
string
Optional UUID of an Orbit user this participant represents. Defaults to the calling user.
contact_id
string
Optional UUID of a contact the participant represents (CRM linkage).
ttl_seconds
integer
Token TTL in seconds (60–86400). Defaults to the service-configured value.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/join" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "identity": "user_42",
  "display_name": "Jane Doe",
  "role": "host"
}'
Node.js
const token = await orbit.video.roomsScheduled.join('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002', {
  identity: 'user_42',
  display_name: 'Jane Doe',
  role: 'host',
})
console.log(token.data.token, token.data.ws_url)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"], "Content-Type": "application/json"}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/join",
                  headers=headers, json={
                    "identity": "user_42",
                    "display_name": "Jane Doe",
                    "role": "host"
                  })
print(r.json())
Go
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/join",
		bytes.NewBuffer([]byte(`{"identity":"user_42","display_name":"Jane Doe","role":"host"}`)))
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/join');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: ' . getenv('ORBIT_API_KEY'),
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"identity":"user_42","display_name":"Jane Doe","role":"host"}');
echo curl_exec($ch);
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "ws_url": "wss://media.orbit.devotel.io",
    "room_sid": "RM_AbCdEfGhIjKl"
  },
  "meta": { "request_id": "req_video_004", "timestamp": "2026-05-10T14:05:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (validation), 404 (room not found), 500 VIDEO_ROOM_JOIN_FAILED.

End a Scheduled Room

POST /api/v1/video/rooms-scheduled/{id}/end Terminate the room immediately. All participants are disconnected and the room transitions to ended. Recording (if active) is stopped and flushed.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/end" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
await orbit.video.roomsScheduled.end('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002')
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/end", headers=headers)
print(r.status_code)
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/end", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/end');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
Error codes: 503 SERVICE_UNAVAILABLE, 400 (id not a UUID), 403 (caller lacks owner/admin/developer), 404 (room not found), 500 VIDEO_ROOM_END_FAILED.

Start Recording

POST /api/v1/video/rooms-scheduled/{id}/recording/start Begin an Orbit Media egress recording on the room. Returns the egress id to track via the Orbit Media webhook lifecycle. If recording_enabled was set at creation time the recording auto-starts and this call is a no-op.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/start" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
const egress = await orbit.video.roomsScheduled.recording.start('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002')
console.log(egress.data.egress_id)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/start", headers=headers)
print(r.json())
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/start", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/start');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
{
  "data": {
    "egress_id": "EG_QrStUvWxYz"
  },
  "meta": { "request_id": "req_video_005", "timestamp": "2026-05-10T14:06:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 403 (caller lacks owner/admin/developer), 404 (room not found), 500 VIDEO_RECORDING_START_FAILED.

Stop Recording

POST /api/v1/video/rooms-scheduled/{id}/recording/stop End the Orbit Media egress. The recording is finalised and the resulting URL (MP4 / HLS depending on egress settings) is written back to the room row asynchronously via the Orbit Media webhook.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/stop" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
await orbit.video.roomsScheduled.recording.stop('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002')
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/stop", headers=headers)
print(r.status_code)
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/stop", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/recording/stop');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
Error codes: 503 SERVICE_UNAVAILABLE, 403, 404 (room not found), 500 VIDEO_RECORDING_STOP_FAILED.

Guest invites

Issue tokenised join links to people outside your tenant. The invite token IS the credential — anyone who holds it can redeem it (subject to expires_at + max_uses) without an Orbit account.

Create an Invite

POST /api/v1/video/rooms-scheduled/{id}/invites Mint a guest-redeemable invite for a scheduled room. The returned invite_token is what you embed in a shareable URL.
participant_name
string
Optional name pre-filled for the guest in the redeem flow (1–120 chars).
expires_in_hours
integer
Invite TTL in hours (1–168). Defaults to a service-configured value (typically 24).
max_uses
integer
Cap on the number of redemptions (1–500). Omit or null for unlimited.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "participant_name": "Acme Customer",
  "expires_in_hours": 24,
  "max_uses": 5
}'
Node.js
const invite = await orbit.video.roomsScheduled.invites.create('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002', {
  participant_name: 'Acme Customer',
  expires_in_hours: 24,
  max_uses: 5,
})
console.log(invite.data.invite_token)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"], "Content-Type": "application/json"}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites",
                  headers=headers, json={
                    "participant_name": "Acme Customer",
                    "expires_in_hours": 24,
                    "max_uses": 5
                  })
print(r.json())
Go
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites",
		bytes.NewBuffer([]byte(`{"participant_name":"Acme Customer","expires_in_hours":24,"max_uses":5}`)))
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: ' . getenv('ORBIT_API_KEY'),
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"participant_name":"Acme Customer","expires_in_hours":24,"max_uses":5}');
echo curl_exec($ch);
{
  "data": {
    "id": "5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14",
    "room_id": "8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002",
    "invite_token": "p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d",
    "participant_name": "Acme Customer",
    "expires_at": "2026-05-11T22:00:00Z",
    "revoked_at": null,
    "max_uses": 5,
    "uses_count": 0,
    "created_at": "2026-05-10T22:00:00Z"
  },
  "meta": { "request_id": "req_video_006", "timestamp": "2026-05-10T22:00:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (validation), 403, 404 (room not found), 500 VIDEO_INVITE_CREATE_FAILED. Rate-limited per the authenticated-write bucket.

List Invites

GET /api/v1/video/rooms-scheduled/{id}/invites Retrieve every invite issued for a room.
include_expired
boolean
default:"false"
When true, also returns invites past expires_at.
limit
integer
default:"100"
Results to return (1–500).
cURL
curl -X GET "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites?include_expired=true" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
const invites = await orbit.video.roomsScheduled.invites.list('8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002', {
  include_expired: true,
})
console.log(invites.data)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites",
                 headers=headers, params={"include_expired": "true"})
print(r.json())
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites?include_expired=true", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites?include_expired=true');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
{
  "data": [
    {
      "id": "5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14",
      "room_id": "8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002",
      "invite_token": "p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d",
      "participant_name": "Acme Customer",
      "expires_at": "2026-05-11T22:00:00Z",
      "revoked_at": null,
      "max_uses": 5,
      "uses_count": 1,
      "created_at": "2026-05-10T22:00:00Z"
    }
  ],
  "meta": { "request_id": "req_video_007", "timestamp": "2026-05-10T22:30:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (invalid query), 404 (room not found), 500 VIDEO_INVITE_LIST_FAILED. Rate-limited per the authenticated-read bucket.

Revoke an Invite

DELETE /api/v1/video/rooms-scheduled/{id}/invites/{inviteId} Permanently revoke an invite. Subsequent redeem attempts return 404.
cURL
curl -X DELETE "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites/5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
await orbit.video.roomsScheduled.invites.revoke(
  '8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002',
  '5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14',
)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.delete("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites/5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14",
                    headers=headers)
print(r.status_code)
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("DELETE", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites/5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/invites/5b3c7f01-2e44-49ab-bce0-7d0a8c9f0b14');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
Error codes: 503 SERVICE_UNAVAILABLE, 400 (id / inviteId not UUIDs), 403, 404 (invite not found), 500 VIDEO_INVITE_REVOKE_FAILED. Rate-limited per the authenticated-write bucket.

Redeem an Invite (public)

POST /api/v1/video/invites/{inviteToken}/redeem No authentication. Guest-facing endpoint. Exchanges an invite token for a short-lived Orbit Media JWT scoped to one room. Rate-limited per IP (30 requests / minute).
inviteToken
string
required
The opaque invite token returned by POST /rooms-scheduled/{id}/invites. 24–64 characters, [A-Za-z0-9_-]+.
display_name
string
required
Friendly name shown to other participants (1–120 chars).
identity
string
Optional URL-safe identity ([A-Za-z0-9_:-]+, 1–120 chars). If omitted, the service mints one.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/invites/p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d/redeem" \
  -H "Content-Type: application/json" \
  -d '{ "display_name": "Acme Customer" }'
Node.js
// Public endpoint — no API key. Call directly via fetch.
const res = await fetch('https://api.orbit.devotel.io/api/v1/video/invites/p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d/redeem', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ display_name: 'Acme Customer' }),
})
const json = await res.json()
console.log(json.data.token, json.data.media_url)
Python
import requests
r = requests.post("https://api.orbit.devotel.io/api/v1/video/invites/p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d/redeem",
                  json={"display_name": "Acme Customer"})
print(r.json())
Go
package main

import (
	"bytes"
	"net/http"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/invites/p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d/redeem",
		bytes.NewBuffer([]byte(`{"display_name":"Acme Customer"}`)))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/invites/p9Z-vQrX_3kT2hN1m4yL5wB6aE7s8C9d/redeem');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"display_name":"Acme Customer"}');
echo curl_exec($ch);
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "ws_url": "wss://media.orbit.devotel.io",
    "media_url": "wss://media.orbit.devotel.io",
    "livekit_url": "wss://media.orbit.devotel.io",
    "room_sid": "RM_AbCdEfGhIjKl",
    "room_id": "8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002",
    "room_name": "Onboarding call - Acme",
    "identity": "guest_8f2a01b7",
    "display_name": "Acme Customer",
    "expires_at": "2026-05-10T15:00:00Z"
  },
  "meta": { "request_id": "req_video_008", "timestamp": "2026-05-10T14:00:00Z" }
}
Error codes: 400 (validation, including malformed token), 404 (invite not found / revoked / expired / max-uses exhausted), 429 (per-IP rate-limit), 500 VIDEO_INVITE_REDEEM_FAILED.

Host admin controls

Force-disconnect or force-mute participants in a live scheduled room.

Kick a Participant

POST /api/v1/video/rooms-scheduled/{id}/participants/{identity}/kick Immediately disconnect a participant from the room. Their Orbit Media session is terminated; they can rejoin only with a fresh token.
identity
string
required
The Orbit Media identity to kick (matches the identity passed at join time).
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/kick" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
await orbit.video.roomsScheduled.participants.kick(
  '8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002',
  'guest_8f2a01b7',
)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/kick",
                  headers=headers)
print(r.status_code)
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/kick", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/kick');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
Error codes: 503 SERVICE_UNAVAILABLE, 400 (id not a UUID), 403, 404 (participant or room not found), 500 VIDEO_PARTICIPANT_KICK_FAILED. Rate-limited per the authenticated-write bucket.

Mute a Participant

POST /api/v1/video/rooms-scheduled/{id}/participants/{identity}/mute Force-mute a participant’s audio or video track. The participant can unmute themselves; this is a host-driven hard mute, not a permanent block.
kind
string
default:"audio"
audio or video. Selects which track to mute.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/mute" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "audio" }'
Node.js
await orbit.video.roomsScheduled.participants.mute(
  '8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002',
  'guest_8f2a01b7',
  { kind: 'audio' },
)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"], "Content-Type": "application/json"}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/mute",
                  headers=headers, json={"kind": "audio"})
print(r.status_code)
Go
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/mute",
		bytes.NewBuffer([]byte(`{"kind":"audio"}`)))
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms-scheduled/8e7c9a02-3e94-4f4f-b2a8-91d6b9b3a002/participants/guest_8f2a01b7/mute');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: ' . getenv('ORBIT_API_KEY'),
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"kind":"audio"}');
echo curl_exec($ch);
Error codes: 503 SERVICE_UNAVAILABLE, 400 (validation, including kind not in ), 403, 404 (participant or room not found), 500 VIDEO_PARTICIPANT_MUTE_FAILED. Rate-limited per the authenticated-write bucket.

Ad-hoc rooms

Fire-and-forget Orbit Media rooms. No DB row, no inbox conversation, no recording. Use when the room only needs to live for one session and disappear when participants leave. Room names are tenant-prefixed on the Orbit Media side (invariant #TI-P0-3) so collisions across tenants are impossible.

Create an Ad-hoc Room

POST /api/v1/video/rooms Create an Orbit Media room and receive a host token in the same response.
name
string
required
URL-safe room name (3–200 chars, [a-zA-Z0-9_-]+). Tenant prefix is prepended internally; display_name echoes the un-prefixed name.
max_participants
integer
Hard cap on concurrent participants (2–100).
metadata
object
Arbitrary key-value pairs forwarded to Orbit Media.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "quick-sync",
  "max_participants": 8
}'
Node.js
const room = await orbit.video.rooms.create({
  name: 'quick-sync',
  max_participants: 8,
})
console.log(room.data.room_name, room.data.token)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"], "Content-Type": "application/json"}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms", headers=headers, json={
  "name": "quick-sync",
  "max_participants": 8
})
print(r.json())
Go
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms",
		bytes.NewBuffer([]byte(`{"name":"quick-sync","max_participants":8}`)))
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: ' . getenv('ORBIT_API_KEY'),
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"name":"quick-sync","max_participants":8}');
echo curl_exec($ch);
{
  "data": {
    "room_name": "tenant_abc1234_quick-sync",
    "display_name": "quick-sync",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "url": "wss://media.orbit.devotel.io"
  },
  "meta": { "request_id": "req_video_009", "timestamp": "2026-05-10T14:00:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (validation, including non-URL-safe name), 403, 500 VIDEO_ROOM_CREATION_FAILED.

List Ad-hoc Rooms

GET /api/v1/video/rooms List active Orbit Media rooms owned by the current tenant. Cursor-paginated by room name.
cursor
string
Room name to resume from. Returned in the previous page’s pagination.next_cursor.
limit
integer
default:"20"
Results per page (max 100).
cURL
curl -X GET "https://api.orbit.devotel.io/api/v1/video/rooms?limit=20" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
const rooms = await orbit.video.rooms.list({ limit: 20 })
console.log(rooms.data, rooms.pagination)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://api.orbit.devotel.io/api/v1/video/rooms", headers=headers, params={"limit": 20})
print(r.json())
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/video/rooms?limit=20", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms?limit=20');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
{
  "data": [
    {
      "name": "tenant_abc1234_quick-sync",
      "display_name": "quick-sync",
      "num_participants": 2,
      "max_participants": 8,
      "creation_time": "2026-05-10T14:00:00Z",
      "metadata": ""
    }
  ],
  "pagination": {
    "next_cursor": null,
    "has_more": false,
    "total": 1
  },
  "meta": { "request_id": "req_video_010", "timestamp": "2026-05-10T14:05:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 INVALID_CURSOR, 500 VIDEO_ROOM_LIST_FAILED.

Get an Ad-hoc Room

GET /api/v1/video/rooms/{name} Fetch a specific ad-hoc room with its live participant roster. Pass either the prefixed wire name or the bare display name.
name
string
required
Room name (with or without the tenant_<id>_ prefix).
cURL
curl -X GET "https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
const detail = await orbit.video.rooms.get('quick-sync')
console.log(detail.data.participants)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.get("https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync", headers=headers)
print(r.json())
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("GET", "https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
{
  "data": {
    "name": "tenant_abc1234_quick-sync",
    "display_name": "quick-sync",
    "num_participants": 2,
    "max_participants": 8,
    "creation_time": "2026-05-10T14:00:00Z",
    "metadata": "",
    "participants": [
      {
        "identity": "user_42",
        "name": "Jane Doe",
        "joined_at": "2026-05-10T14:00:30Z",
        "is_publishing": true
      }
    ]
  },
  "meta": { "request_id": "req_video_011", "timestamp": "2026-05-10T14:02:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 404 NOT_FOUND, 500 VIDEO_ROOM_DETAILS_FAILED.

Delete an Ad-hoc Room

DELETE /api/v1/video/rooms/{name} Close the Orbit Media room and disconnect every participant.
cURL
curl -X DELETE "https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync" \
  -H "X-API-Key: dv_live_sk_your_key_here"
Node.js
await orbit.video.rooms.delete('quick-sync')
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"]}
r = requests.delete("https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync", headers=headers)
print(r.status_code)
Go
package main

import (
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("DELETE", "https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync", nil)
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . getenv('ORBIT_API_KEY')]);
echo curl_exec($ch);
Error codes: 503 SERVICE_UNAVAILABLE, 403 (caller lacks owner/admin/developer), 500 VIDEO_ROOM_CLOSE_FAILED.

Issue a Participant Token

POST /api/v1/video/rooms/{name}/token Generate a per-participant Orbit Media join token for an ad-hoc room. Use this when the host needs to invite someone else into the room they created.
participant_name
string
required
Friendly participant name (1–200 chars). Also used as the Orbit Media identity.
metadata
object
Arbitrary key-value pairs attached to the Orbit Media participant.
cURL
curl -X POST "https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync/token" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "participant_name": "Jane Doe" }'
Node.js
const token = await orbit.video.rooms.token('quick-sync', { participant_name: 'Jane Doe' })
console.log(token.data.token, token.data.url)
Python
import os, requests
headers = {"X-API-Key": os.environ["ORBIT_API_KEY"], "Content-Type": "application/json"}
r = requests.post("https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync/token",
                  headers=headers, json={"participant_name": "Jane Doe"})
print(r.json())
Go
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	req, _ := http.NewRequest("POST", "https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync/token",
		bytes.NewBuffer([]byte(`{"participant_name":"Jane Doe"}`)))
	req.Header.Set("X-API-Key", os.Getenv("ORBIT_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	http.DefaultClient.Do(req)
}
PHP
<?php
$ch = curl_init('https://api.orbit.devotel.io/api/v1/video/rooms/quick-sync/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'X-API-Key: ' . getenv('ORBIT_API_KEY'),
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"participant_name":"Jane Doe"}');
echo curl_exec($ch);
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "url": "wss://media.orbit.devotel.io"
  },
  "meta": { "request_id": "req_video_012", "timestamp": "2026-05-10T14:01:00Z" }
}
Error codes: 503 SERVICE_UNAVAILABLE, 400 (validation), 403, 500 TOKEN_GENERATION_FAILED.

Inbound webhook

Orbit Media posts room and recording lifecycle events to POST /api/v1/video/webhook. The endpoint is platform-managed — you don’t call it directly. Events received here drive started_at / ended_at / recording_url updates on video_rooms and the matching inbox-conversation status transitions. Orbit Media signs each delivery with a JWT minted from the API secret; Orbit validates the signature via the SFU’s WebhookReceiver (carried over from the LiveKit OSS fork) before processing.

See also