Getting started
Go from zero to a working browser video session: provision a room, mint a join token on your server, and connect with the Sendexa Video Web SDK.
Enable Live Video
Turn on Live Video for your business in the dashboard and copy your Basic Auth token.
Create a room
POST /v1/video/rooms from your backend with optional name, capacity, and record flag.
Issue join tokens
Mint short-lived host/participant/viewer tokens per user — never expose REST credentials.
Embed the Web SDK
Load the Sendexa Video client, pass roomId + token, and connect with media constraints.
api.sendexa.co
HTTPS only
Basic
Dashboard Base64 token
JWT
Join token per identity
<2 min
With valid credentials
Authentication
Authorization: Basic <token>. Auth guide →- A Sendexa business account with Live Video enabled (Preview)
- Dashboard API token (Basic Auth) used only on the server
- HTTPS origin for the browser page (required for getUserMedia)
- Modern browser with WebRTC (Chrome, Firefox, Edge, Safari 14+)
Step 1 — Create a room
Rooms are the isolation boundary for a session. Create one when a meeting, consult, or class starts. See Rooms & sessions for the full reference.
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | optional | Human-readable label (max 128 chars). Shown in dashboard logs. |
| maxParticipants | integer | optional | Capacity including host. Default 12, max 50. |
| record | boolean | optional | If true, composite cloud recording starts when the room becomes active. |
| metadata | object | optional | Arbitrary JSON (≤ 4 KB) echoed in GET room and webhooks. |
curl -X POST 'https://api.sendexa.co/v1/video/rooms' \-H 'Content-Type: application/json' \-H 'Authorization: Basic YOUR_DASHBOARD_BASE64_TOKEN' \-d '{"name": "Onboarding Call — Ama K.","maxParticipants": 3,"record": false,"metadata": {"appointmentId": "apt_991","product": "kyc-video"}}'
{"success": true,"code": "VIDEO_ROOM_CREATED","data": {"roomId": "rm_8f3a2c1b0e9d","name": "Onboarding Call — Ama K.","status": "ready","maxParticipants": 3,"participantCount": 0,"record": false,"region": "af-west","metadata": {"appointmentId": "apt_991","product": "kyc-video"},"createdAt": "2026-07-12T11:00:00.000Z","emptyTimeoutSeconds": 300}}
Empty timeout
emptyTimeoutSeconds (default 300), the room transitions to ended automatically. Create a new room for a later session rather than reusing stale IDs.Step 2 — Issue a join token
Tokens authorize a single identity to join a specific room with a role and expiry. Generate them only on your backend after authenticating the end user. Full docs: Tokens & auth.
| Field | Type | Required | Description |
|---|---|---|---|
| identity | string | required | Stable user identifier in your system (e.g. user_42). Max 64 chars, unique per concurrent join. |
| role | string | required | One of: host | participant | viewer. |
| ttl | integer | optional | Lifetime in seconds. Default 3600. Min 60, max 86400. |
curl -X POST 'https://api.sendexa.co/v1/video/rooms/rm_8f3a2c1b0e9d/tokens' \-H 'Content-Type: application/json' \-H 'Authorization: Basic YOUR_DASHBOARD_BASE64_TOKEN' \-d '{"identity": "user_ama","role": "participant","ttl": 1800}'
{"success": true,"code": "VIDEO_TOKEN_ISSUED","data": {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","roomId": "rm_8f3a2c1b0e9d","identity": "user_ama","role": "participant","expiresAt": "2026-07-12T11:30:00.000Z","ttl": 1800}}
Step 3 — Embed the Web SDK
The browser client never calls the room management API. It only needs roomId and the join token from your app backend.
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><title>Sendexa Live Video</title><script src="https://cdn.sendexa.co/video/v1/sendexa-video.min.js"></script><style>#stage { width: 100%; max-width: 960px; aspect-ratio: 16/9; background: #0b0f19; }#local, #remote-grid video { width: 100%; height: 100%; object-fit: cover; }</style></head><body><div id="stage"><video id="local" autoplay muted playsinline></video><div id="remote-grid"></div></div><button id="join">Join</button><button id="leave">Leave</button><button id="toggle-mic">Mic</button><button id="toggle-cam">Camera</button><button id="share">Share screen</button><script>// Injected securely from your backend (do not hardcode secrets)const ROOM_ID = "rm_8f3a2c1b0e9d";const TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";const room = new SendexaVideo.Room({roomId: ROOM_ID,token: TOKEN,// Optional: prefer a region if the room was created without sticky pin// region: "af-west",media: {audio: true,video: {width: { ideal: 1280 },height: { ideal: 720 },frameRate: { ideal: 24, max: 30 },facingMode: "user"}}});room.on("localTrack", ({ track, kind }) => {if (kind === "video") {track.attach(document.getElementById("local"));}});room.on("participantConnected", (participant) => {participant.tracks.forEach((pub) => {if (pub.track) attachRemote(pub.track, participant.identity);});participant.on("trackSubscribed", (track) => {attachRemote(track, participant.identity);});});room.on("disconnected", (reason) => {console.log("left room", reason);});document.getElementById("join").onclick = () => room.connect();document.getElementById("leave").onclick = () => room.disconnect();document.getElementById("toggle-mic").onclick = () => room.localParticipant.setMicrophoneEnabled(!room.localParticipant.isMicrophoneEnabled);document.getElementById("toggle-cam").onclick = () => room.localParticipant.setCameraEnabled(!room.localParticipant.isCameraEnabled);document.getElementById("share").onclick = () => room.localParticipant.setScreenShareEnabled(true);function attachRemote(track, identity) {if (track.kind !== "video" && track.kind !== "audio") return;let el = document.getElementById("remote-" + identity);if (!el) {el = document.createElement(track.kind === "video" ? "video" : "audio");el.id = "remote-" + identity;el.autoplay = true;el.playsInline = true;document.getElementById("remote-grid").appendChild(el);}track.attach(el);}</script></body></html>
npm install @sendexa/video-client
import { Room } from '@sendexa/video-client';const room = new Room({roomId,token,media: {audio: { echoCancellation: true, noiseSuppression: true },video: { width: 1280, height: 720, frameRate: 24 },},});await room.connect();// room.localParticipant, room.participants, room.disconnect()
Media constraints
Constraints map to the browser MediaTrackConstraints model. The SFU may downscale or simulcast based on subscriber bandwidth; requested values are ideals, not guarantees.
Audio
echoCancellation, noiseSuppression, autoGainControl — enable for voice calls.
Video
Ideal 720p @ 24 fps for most apps; 360p for low bandwidth; max 1080p in Preview.
Screen share
Hosts and participants can publish a screen track; viewers cannot publish.
Adaptive bitrate
Simulcast layers let remote clients subscribe at a bitrate matching their network.
// Recommended defaults by scenarioconst constraints = {// 1:1 support / telehealthcall: {audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },video: { width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 24 } },},// Low bandwidth / mobile dataeconomy: {audio: true,video: { width: { ideal: 640 }, height: { ideal: 360 }, frameRate: { ideal: 15, max: 20 } },},// Audio-only fallbackaudioOnly: {audio: true,video: false,},// Webinar hosthost: {audio: true,video: { width: { ideal: 1920 }, height: { ideal: 1080 }, frameRate: { ideal: 30 } },},};// Viewer role should join with publish disabled (SDK honors token role)const viewerMedia = { audio: false, video: false };
Permissions & HTTPS
NotAllowedError and NotFoundError from getUserMedia and show clear UI when devices are missing or blocked.Regions
At room creation, Sendexa selects a media region (or you pin one when the API supports region). All participants for that room use the same SFU cluster so mesh complexity stays low.
| Region ID | Recommended for | Notes |
|---|---|---|
| af-west | Ghana, Nigeria, West Africa | Default for most Sendexa accounts |
| af-south | South Africa & neighbours | Lower RTT for ZA endpoints |
| eu-west | Europe & UK diaspora | Good cross-Atlantic compromise |
| us-east | Americas | Use when majority of users are in US |
Choose the region closest to the majority of participants — not necessarily the host — for best average experience.
- ✓ Rooms and tokens created only from backend services
- ✓ Join tokens short-lived; identity maps to your user IDs
- ✓ Webhook endpoint registered for room and recording events
- ✓ HTTPS app origin; permission UX for camera/mic denial
- ✓ Graceful end via
POST /v1/video/rooms/:roomId/end - ✓ Client disconnect on page unload / SPA route change