twilio-video

Enable OpenClaw to design, implement, operate, and troubleshoot Twilio Video in production:

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "twilio-video" with this command: npx skills add alphaonedev/openclaw-graph/alphaonedev-openclaw-graph-twilio-video

twilio-video

Purpose

Enable OpenClaw to design, implement, operate, and troubleshoot Twilio Video in production:

  • Create and manage Video Rooms (Group and Peer-to-Peer), including lifecycle controls.

  • Issue Access Tokens with Video grants (identity, room scoping, TTL, key rotation).

  • Publish/subscribe tracks (audio/video/data), manage track priorities, and implement moderation (mute/unpublish/remove participants).

  • Enable and operate recording via Compositions (server-side recording outputs), including callbacks and storage pipelines.

  • Use Network Quality API and bandwidth profiles to maintain call quality under real-world network constraints.

  • Integrate Video with Twilio cluster patterns: webhooks, auth, rate limits, cost controls, and operational playbooks.

This guide assumes you are building a multi-tenant, production WebRTC system with compliance, observability, and incident response requirements.

Prerequisites

Twilio account + credentials

  • Twilio Account SID (format AC... )

  • Twilio API Key SID (format SK... ) and Secret

  • (Optional) Twilio Auth Token (format ... ) for legacy/basic auth; prefer API Key + Secret for server-to-server.

  • A Twilio Video-enabled project (default for most accounts).

Create API Key:

twilio api:core:keys:create --friendly-name "video-prod-key-2026-02"

Supported SDKs / libraries (pin versions)

Server-side (token minting, REST API calls):

  • Node.js: node >= 20.11.1 (LTS), npm >= 10.2.4

  • twilio@4.23.0 (Twilio Node helper library)

  • jsonwebtoken@9.0.2 (if you implement custom JWT handling; Twilio helper can mint tokens)

  • Python: python >= 3.11.7

  • twilio==9.4.1

  • Go: go >= 1.22.1

  • github.com/twilio/twilio-go@v1.20.3

Client-side:

  • Twilio Video JS SDK: twilio-video@2.31.1

  • Browser support: latest Chrome/Edge, Firefox ESR, Safari 17+ (macOS/iOS constraints apply)

  • iOS: TwilioVideo iOS SDK 5.10.0 (CocoaPods/SPM)

  • Android: Twilio Video Android SDK 7.6.0 (Gradle)

Twilio CLI:

  • twilio-cli@5.5.1 (Node-based)

  • Plugins:

  • @twilio-labs/plugin-video@0.7.0 (Video commands; plugin availability varies—verify in your environment)

Install Twilio CLI:

npm i -g twilio-cli@5.5.1 twilio --version

Install plugin:

twilio plugins:install @twilio-labs/plugin-video@0.7.0 twilio plugins

Auth setup (local dev + CI)

Prefer environment variables:

  • TWILIO_ACCOUNT_SID

  • TWILIO_API_KEY

  • TWILIO_API_SECRET

  • (Optional) TWILIO_AUTH_TOKEN (avoid in CI if possible)

Example (bash):

export TWILIO_ACCOUNT_SID="YOUR_ACCOUNT_SID" export TWILIO_API_KEY="YOUR_API_KEY_SID" export TWILIO_API_SECRET="your_api_secret_from_console"

Twilio CLI login (stores credentials locally; not recommended for CI runners):

twilio login

Network + infra prerequisites

  • Public HTTPS endpoint for webhooks (Composition callbacks, Status callbacks).

  • TLS: modern ciphers; certificate from a trusted CA (Let’s Encrypt OK).

  • Time sync: NTP enabled on servers minting JWTs (clock skew breaks tokens).

  • TURN/STUN: Twilio provides; ensure outbound UDP/TCP 3478/5349 allowed; enterprise networks may require TCP/TLS fallback.

Core Concepts

Rooms: Group vs Peer-to-Peer (P2P)

  • Group Room: media routed via Twilio’s SFU; supports larger rooms, recording/compositions, bandwidth profiles, network quality, dominant speaker, etc. Default for most production use.

  • Peer-to-Peer Room: direct mesh between participants; limited scalability; fewer server-side features; generally not recommended for production beyond 1:1 with strict latency constraints.

Key properties:

  • type : group | group-small | peer-to-peer (availability depends on account/region)

  • status : in-progress | completed

  • maxParticipants : enforce caps to control cost and quality

Participants and Tracks

  • Participant: identity in a room (unique per room).

  • Tracks:

  • Audio track

  • Video track

  • Data track (reliable/unreliable messaging)

  • Track lifecycle: published → subscribed (remote) → unpublished / stopped

  • Server-side moderation often means:

  • Disconnect participant

  • Force unpublish (where supported)

  • Enforce client behavior via signaling + policy

Access Tokens (JWT)

Twilio Video uses JWT access tokens with a VideoGrant:

  • identity : stable user identifier (tenant-scoped)

  • ttl : seconds; keep short (e.g., 3600) and refresh

  • room : optional; restrict token to a specific room

  • Signed with API Key Secret

Clock skew is a common failure mode; keep NTP.

Recording via Compositions

Twilio Video “recording” in production typically means Compositions:

  • A Composition is a server-side rendered output (e.g., MP4) created from recorded tracks.

  • You can configure:

  • Layout (grid, active speaker, custom)

  • Format

  • Status callback URL

  • You must handle asynchronous completion and storage (S3/GCS/Azure) yourself.

Network Quality API

  • Provides per-participant network quality levels (0–5) and stats.

  • Use it to:

  • Adapt bitrate/resolution

  • Trigger UI warnings

  • Decide when to switch to audio-only

  • Combine with bandwidth profiles for predictable behavior.

Bandwidth Profiles and Track Priority

  • Bandwidth profiles define how Twilio allocates bandwidth among tracks.

  • Track priority (low /standard /high ) influences which tracks degrade first.

  • Use for:

  • Prioritizing presenter video

  • Keeping audio stable under congestion

Webhooks and callbacks

Common callback types:

  • Composition status callbacks

  • Room status callbacks (where configured)

  • Participant events (often handled client-side; server can poll REST API)

Webhooks must be:

  • HTTPS

  • Verified (Twilio signature validation)

  • Idempotent (Twilio retries)

Installation & Setup

Official Python SDK — Video

Repository: https://github.com/twilio/twilio-python

PyPI: pip install twilio · Supported: Python 3.7–3.13

from twilio.rest import Client from twilio.jwt.access_token import AccessToken from twilio.jwt.access_token.grants import VideoGrant

client = Client()

Create a room

room = client.video.v1.rooms.create( unique_name="DailyStandup", type="go" # or 'group' / 'peer-to-peer' ) print(room.sid)

Generate participant access token

token = AccessToken( os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_API_KEY"], os.environ["TWILIO_API_SECRET"], identity="alice" ) token.add_grant(VideoGrant(room="DailyStandup")) print(token.to_jwt())

Source: twilio/twilio-python — video

Ubuntu 22.04 LTS (x86_64)

sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg jq curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs node -v npm -v

sudo npm i -g twilio-cli@5.5.1 twilio --version twilio plugins:install @twilio-labs/plugin-video@0.7.0

Fedora 39/40 (x86_64)

sudo dnf install -y nodejs npm jq node -v npm -v

sudo npm i -g twilio-cli@5.5.1 twilio plugins:install @twilio-labs/plugin-video@0.7.0

macOS (Intel + Apple Silicon)

Using Homebrew:

brew install node@20 jq echo 'export PATH="/opt/homebrew/opt/node@20/bin:$PATH"' >> ~/.zshrc # Apple Silicon echo 'export PATH="/usr/local/opt/node@20/bin:$PATH"' >> ~/.zshrc # Intel source ~/.zshrc

npm i -g twilio-cli@5.5.1 twilio plugins:install @twilio-labs/plugin-video@0.7.0

Windows 11 (PowerShell)

Install Node.js 20 LTS from official installer or winget:

winget install OpenJS.NodeJS.LTS node -v npm -v npm i -g twilio-cli@5.5.1 twilio --version twilio plugins:install @twilio-labs/plugin-video@0.7.0

Server library setup (Node.js)

mkdir -p video-service && cd video-service npm init -y npm i twilio@4.23.0 fastify@4.26.2 @fastify/env@4.3.0 pino@8.19.0 npm i -D typescript@5.3.3 tsx@4.7.0 @types/node@20.11.19

Minimal tsconfig.json :

{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, "esModuleInterop": true, "skipLibCheck": true } }

Server library setup (Python)

python3.11 -m venv .venv source .venv/bin/activate pip install --upgrade pip==24.0 pip install twilio==9.4.1 fastapi==0.109.2 uvicorn==0.27.1 python-dotenv==1.0.1

Key Capabilities

Room lifecycle management

Production patterns:

  • Create rooms on-demand (or pre-provision for scheduled sessions).

  • Enforce uniqueName naming conventions: include tenant + resource id.

  • Set maxParticipants to control cost and prevent abuse.

  • Complete rooms explicitly when sessions end to stop billing.

Example naming scheme:

  • acme-prod:class:9f3c2b1a

  • tenantId:resourceType:resourceId

Node: create room:

import twilio from "twilio";

const client = twilio(process.env.TWILIO_API_KEY!, process.env.TWILIO_API_SECRET!, { accountSid: process.env.TWILIO_ACCOUNT_SID!, });

async function createRoom() { const room = await client.video.v1.rooms.create({ uniqueName: "acme-prod:class:9f3c2b1a", type: "group", maxParticipants: 25, recordParticipantsOnConnect: false }); return room; }

Complete room:

await client.video.v1.rooms("RM0123456789abcdef0123456789abcdef") .update({ status: "completed" });

Access token minting (VideoGrant)

Token service requirements:

  • Authenticate caller (your auth, not Twilio’s).

  • Authorize access to a room (tenant + role checks).

  • Mint short-lived token (e.g., 15–60 minutes).

  • Rotate API keys without downtime (support multiple signing keys).

Node token minting:

import twilio from "twilio";

const { AccessToken } = twilio.jwt; const { VideoGrant } = AccessToken;

export function mintVideoToken(params: { identity: string; room?: string; ttlSeconds?: number; }) { const token = new AccessToken( process.env.TWILIO_ACCOUNT_SID!, process.env.TWILIO_API_KEY!, process.env.TWILIO_API_SECRET!, { identity: params.identity, ttl: params.ttlSeconds ?? 3600 } );

token.addGrant(new VideoGrant({ room: params.room })); return token.toJwt(); }

Python token minting:

from twilio.jwt.access_token import AccessToken from twilio.jwt.access_token.grants import VideoGrant

def mint_video_token(identity: str, room: str | None = None, ttl: int = 3600) -> str: token = AccessToken( account_sid=os.environ["TWILIO_ACCOUNT_SID"], signing_key_sid=os.environ["TWILIO_API_KEY"], secret=os.environ["TWILIO_API_SECRET"], identity=identity, ttl=ttl, ) token.add_grant(VideoGrant(room=room)) return token.to_jwt()

Track publication, subscription, and moderation

Client-side (JS) connect with bandwidth profile and dominant speaker:

import Video from "twilio-video";

const room = await Video.connect(token, { name: "acme-prod:class:9f3c2b1a", dominantSpeaker: true, networkQuality: { local: 1, remote: 1 }, bandwidthProfile: { video: { mode: "collaboration", dominantSpeakerPriority: "high", maxSubscriptionBitrate: 2500000, renderDimensions: { high: { width: 1280, height: 720 }, standard: { width: 640, height: 360 }, low: { width: 320, height: 180 } } } } });

Moderation patterns:

  • “Mute” is typically enforced by client policy (disable local track) + server-side role enforcement.

  • For hard enforcement, disconnect participant:

await client.video.v1.rooms("RM...").participants("PA...") .update({ status: "disconnected" });

Recording and Compositions

Typical flow:

  • Enable participant recording (if required) or rely on composition sources.

  • Create Composition when room completes (or on-demand).

  • Receive status callback (processing → completed /failed ).

  • Fetch composition media URL and ingest into storage.

Create composition (Node):

const composition = await client.video.v1.compositions.create({ roomSid: "RM0123456789abcdef0123456789abcdef", audioSources: "", videoLayout: { grid: { video_sources: [""] } }, format: "mp4", statusCallback: "https://video.acme.com/twilio/composition-status", statusCallbackMethod: "POST" });

Fetch composition media:

const c = await client.video.v1.compositions("CJ0123456789abcdef0123456789abcdef").fetch(); console.log(c.links?.media);

Network Quality API usage

Client-side event handling:

room.on("networkQualityLevelChanged", (level, stats) => { // level: 0..5 // stats: { audio: { send, recv }, video: { send, recv } } depending on SDK if (level <= 2) { console.warn("Network degraded", level, stats); } });

Server-side: fetch participant network quality (where supported by REST API fields; otherwise rely on client telemetry).

Bandwidth and codec tuning

Production defaults:

  • Prefer VP8 for broad compatibility; consider H.264 for Safari-heavy audiences.

  • Use maxSubscriptionBitrate to cap downstream usage.

  • Use track priority for presenter:

const videoTrack = Array.from(room.localParticipant.videoTracks.values())[0]?.track; videoTrack?.setPriority("high");

Webhook handling (Composition callbacks)

Requirements:

  • Validate Twilio signature (X-Twilio-Signature )

  • Idempotency: dedupe by CompositionSid

  • Status
  • Retry-safe: Twilio retries on non-2xx

Fastify example:

import Fastify from "fastify"; import twilio from "twilio";

const app = Fastify({ logger: true });

app.post("/twilio/composition-status", async (req, reply) => { const signature = req.headers["x-twilio-signature"] as string | undefined; const url = "https://video.acme.com/twilio/composition-status";

const isValid = twilio.validateRequest( process.env.TWILIO_AUTH_TOKEN!, // signature validation uses Auth Token signature ?? "", url, req.body as Record<string, string> );

if (!isValid) return reply.code(403).send({ error: "invalid signature" });

// process CompositionSid, Status, RoomSid, etc. return reply.code(204).send(); });

app.listen({ port: 3000, host: "0.0.0.0" });

Note: Signature validation uses Auth Token, not API Secret. If you avoid Auth Token in services, isolate webhook verification into a small component with restricted secret access.

Command Reference

Notes:

  • Twilio CLI command availability depends on installed plugins and Twilio CLI version.

  • For Video, many operations are easiest via REST API using helper libraries; CLI is useful for inspection.

Twilio CLI: global flags

Common across commands:

  • -o, --output [json|tsv|table] output format

  • --properties <props> comma-separated properties to display

  • --no-header omit header (tsv/table)

  • --profile <name> use a named profile from ~/.twilio-cli/config.json

  • --log-level [debug|info|warn|error]

Example:

twilio api:video:v1:rooms:list -o json --log-level debug

Rooms (REST via CLI)

List rooms:

twilio api:video:v1:rooms:list
--status in-progress
--limit 50
-o table
--properties sid,uniqueName,status,type,maxParticipants,dateCreated

Relevant flags:

  • --status <in-progress|completed>

  • --unique-name <string> (filter; if supported by CLI generator)

  • --limit <int>

Fetch room:

twilio api:video:v1:rooms:fetch --sid RM0123456789abcdef0123456789abcdef -o json

Create room:

twilio api:video:v1:rooms:create
--unique-name "acme-prod:class:9f3c2b1a"
--type group
--max-participants 25
--record-participants-on-connect false
-o json

Update room (complete):

twilio api:video:v1:rooms:update
--sid RM0123456789abcdef0123456789abcdef
--status completed
-o json

Participants

List participants in a room:

twilio api:video:v1:rooms:participants:list
--room-sid RM0123456789abcdef0123456789abcdef
--status connected
--limit 200
-o table
--properties sid,identity,status,dateCreated

Flags:

  • --room-sid <RM...>

  • --status <connected|disconnected>

  • --identity <string> (if supported)

  • --limit <int>

Disconnect participant:

twilio api:video:v1:rooms:participants:update
--room-sid RM0123456789abcdef0123456789abcdef
--sid PA0123456789abcdef0123456789abcdef
--status disconnected
-o json

Compositions

Create composition:

twilio api:video:v1:compositions:create
--room-sid RM0123456789abcdef0123456789abcdef
--audio-sources "*"
--format mp4
--status-callback "https://video.acme.com/twilio/composition-status"
--status-callback-method POST
-o json

Fetch composition:

twilio api:video:v1:compositions:fetch
--sid CJ0123456789abcdef0123456789abcdef
-o json

List compositions:

twilio api:video:v1:compositions:list
--room-sid RM0123456789abcdef0123456789abcdef
--limit 20
-o table
--properties sid,status,format,dateCreated

Recordings (if applicable to your account/features)

List recordings for a room:

twilio api:video:v1:recordings:list
--grouping-sid RM0123456789abcdef0123456789abcdef
--limit 50
-o table
--properties sid,status,trackName,dateCreated

API Keys (rotation support)

Create key:

twilio api:core:keys:create --friendly-name "video-key-rotation-2026-02" -o json

List keys:

twilio api:core:keys:list -o table --properties sid,friendlyName,dateCreated

Revoke key:

twilio api:core:keys:remove --sid YOUR_API_KEY_SID

Configuration Reference

  1. OpenClaw service env file

Path (Linux systemd pattern):

  • /etc/openclaw/video-service.env

Example:

TWILIO_ACCOUNT_SID=YOUR_ACCOUNT_SID TWILIO_API_KEY=YOUR_API_KEY_SID TWILIO_API_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy

VIDEO_TOKEN_TTL_SECONDS=3600 VIDEO_ROOM_TYPE=group VIDEO_MAX_PARTICIPANTS=25

PUBLIC_BASE_URL=https://video.acme.com COMPOSITION_STATUS_CALLBACK_URL=https://video.acme.com/twilio/composition-status

LOG_LEVEL=info

  1. systemd unit

Path:

  • /etc/systemd/system/openclaw-video.service

[Unit] Description=OpenClaw Twilio Video Service After=network-online.target Wants=network-online.target

[Service] Type=simple EnvironmentFile=/etc/openclaw/video-service.env WorkingDirectory=/opt/openclaw/video-service ExecStart=/usr/bin/node /opt/openclaw/video-service/dist/server.js Restart=on-failure RestartSec=2 User=openclaw Group=openclaw NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/openclaw-video AmbientCapabilities= CapabilityBoundingSet= LockPersonality=true MemoryDenyWriteExecute=true

[Install] WantedBy=multi-user.target

  1. NGINX reverse proxy (TLS + webhook path)

Path:

  • /etc/nginx/sites-available/video.acme.com

  • symlink to /etc/nginx/sites-enabled/video.acme.com

server { listen 443 ssl http2; server_name video.acme.com;

ssl_certificate /etc/letsencrypt/live/video.acme.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/video.acme.com/privkey.pem;

client_max_body_size 2m;

location /twilio/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 30s; }

location /healthz { proxy_pass http://127.0.0.1:3000/healthz; } }

  1. Twilio CLI config

Path:

  • ~/.twilio-cli/config.json

Example with profiles:

{ "activeProfile": "prod", "profiles": { "prod": { "accountSid": "YOUR_ACCOUNT_SID", "apiKeySid": "YOUR_API_KEY_SID", "apiKeySecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, "staging": { "accountSid": "YOUR_ACCOUNT_SID", "apiKeySid": "YOUR_API_KEY_SID", "apiKeySecret": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" } } }

Integration Patterns

Compose with Twilio Verify (step-up auth before joining a room)

Pattern:

  • User logs in with your primary auth.

  • For high-risk actions (joining a paid session, recording-enabled room), require Verify challenge.

  • Only mint Video token after Verify success.

Pseudo-flow:

  • POST /verify/start → Twilio Verify V2

  • POST /verify/check

  • POST /video/token (requires verified session)

Operational notes:

  • Rate limit token minting per user/IP.

  • Store Verify decision with TTL (e.g., 10 minutes).

Compose with Programmable Messaging (session notifications)

Use Messaging to send:

  • “Room starting” reminders

  • “Recording available” link after composition completes

Production requirements:

  • Handle STOP/opt-out

  • Status callbacks for delivery failures

  • 10DLC registration (US A2P) if using long codes

Pipeline example:

  • Composition callback completed

  • Store media in S3

  • Send SMS with link

  • Track delivery via status webhook

Compose with SendGrid (recording delivery + audit)

SendGrid transactional email:

  • Dynamic template with recording link, session metadata, retention policy

  • Handle bounces/spam; suppress invalid recipients

Compose with Studio (orchestration)

Use Studio Flow to orchestrate:

  • Pre-session reminders

  • Post-session surveys

  • Escalation to support if composition fails

Trigger Studio via REST Trigger API from your backend when room completes.

CI/CD pipeline (key rotation + smoke tests)

  • Rotate API keys monthly:

  • Create new key

  • Deploy with dual-key support (active + next)

  • Switch signing key

  • Revoke old key after TTL window

Smoke test script:

  • Create room

  • Mint token

  • (Optional) headless connect test (Playwright) for JS SDK

  • Complete room

  • Create composition

  • Verify callback received

Error Handling & Troubleshooting

Include these in runbooks; ensure logs capture Twilio request IDs.

  1. Auth failure (bad credentials)

Error (REST):

TwilioRestException: Authenticate HTTP 401 {"code":20003,"message":"Authenticate","more_info":"https://www.twilio.com/docs/errors/20003","status":401}

Root causes:

  • Wrong API Key/Secret

  • Using API Key SID with Auth Token as password

  • Account SID mismatch in client initialization

Fix:

  • Verify TWILIO_ACCOUNT_SID , TWILIO_API_KEY , TWILIO_API_SECRET

  • Ensure helper library is initialized with accountSid when using API keys

  • Rotate compromised keys

  1. Rate limiting

Error:

TwilioRestException: Too Many Requests HTTP 429 {"code":20429,"message":"Too Many Requests","more_info":"https://www.twilio.com/docs/errors/20429","status":429}

Root causes:

  • Bursty room/participant polling

  • Excessive composition creation retries

Fix:

  • Add exponential backoff + jitter

  • Cache room state; avoid tight polling loops

  • Batch operations; reduce list calls

  • Implement circuit breaker for Twilio API

  1. Invalid room name / duplicate uniqueName

Error (typical):

TwilioRestException: The requested resource /Rooms was not found HTTP 404

Or validation errors depending on endpoint. Another common failure is attempting to create a room with an existing uniqueName while it’s in-progress.

Root causes:

  • Reusing uniqueName for concurrent sessions

  • Not completing rooms

Fix:

  • Use unique per-session names (include timestamp/UUID)

  • Complete rooms on session end

  • If you need stable names, fetch existing room by uniqueName and reuse only if completed

  1. Token rejected (clock skew / expired)

Client error (JS SDK):

TwilioError: Access Token expired or invalid

Root causes:

  • Server clock skew

  • TTL too short

  • Token minted with wrong signing key secret

Fix:

  • Ensure NTP on token service

  • Increase TTL to 3600 and refresh proactively

  • Validate key rotation logic

  1. Webhook signature validation fails

Your service logs:

invalid signature

Root causes:

  • Using wrong URL in validation (must match exact public URL)

  • Missing/incorrect TWILIO_AUTH_TOKEN

  • Proxy modifies URL/host; mismatch between internal and external URL

Fix:

  • Use externally visible URL (including scheme/host/path) in validation

  • Ensure NGINX sets Host and X-Forwarded-Proto

  • Log computed URL and compare to Twilio request

  1. Composition stuck in processing / never completes

Composition status remains processing for extended time.

Root causes:

  • Very large room duration

  • Layout configuration issues

  • Source tracks missing or ended unexpectedly

  • Twilio-side processing delays

Fix:

  • Ensure room completed before composition creation (or accept longer processing)

  • Use audioSources: "" and video_sources: [""] for broad capture

  • Add watchdog: if processing

threshold (e.g., 2 hours), alert and open Twilio support ticket with Composition SID

  1. Participant cannot connect behind corporate firewall

Client logs show ICE failures; typical symptom: “Connecting…” then disconnect.

Root causes:

  • UDP blocked; TURN over TCP/TLS required

  • Deep packet inspection interfering with WebRTC

Fix:

  • Ensure outbound TCP 443 allowed (Twilio TURN over TLS)

  • Provide guidance to allowlist Twilio domains

  • Consider fallback to audio-only or PSTN (Twilio Voice) for extreme networks

  1. Media permissions denied (browser)

Browser error:

NotAllowedError: Permission denied

Root causes:

  • User denied mic/camera

  • Insecure context (not HTTPS)

  • iOS Safari requires user gesture to start capture

Fix:

  • Enforce HTTPS

  • Prompt with clear UI and retry

  • Request permissions after user interaction

  1. “Participant identity already exists” (duplicate identity in same room)

Some SDKs surface:

TwilioError: Participant identity is already in use

Root causes:

  • Reconnecting with same identity without disconnecting old session

  • Multi-tab join without unique identity suffix

Fix:

  • Enforce single session per identity (kick old participant)

  • Use identity scheme: userId:deviceId and map to user in app layer

  1. 21211 invalid To (cluster pattern relevance)

If you send SMS notifications and see:

{"code":21211,"message":"The 'To' number +1555... is not a valid phone number.","status":400}

Fix:

  • Normalize E.164

  • Validate phone numbers before sending

  • Store verified numbers only

Security Hardening

Secrets management

  • Store TWILIO_API_SECRET and TWILIO_AUTH_TOKEN in a secrets manager:

  • AWS Secrets Manager / GCP Secret Manager / Vault

  • Do not bake secrets into container images.

  • Rotate API keys regularly; revoke unused keys.

Webhook verification

  • Always validate X-Twilio-Signature .

  • Enforce strict path matching; reject unexpected methods.

  • Implement idempotency keys:

  • CompositionSid + Status unique constraint in DB

Least privilege and isolation

  • Separate services:

  • Token minting service (API Key Secret)

  • Webhook verification service (Auth Token)

  • Use distinct Twilio API keys per environment and per service.

TLS and headers

  • Enforce TLS 1.2+ (prefer TLS 1.3).

  • HSTS for your domain.

  • For web apps: CSP, Permissions-Policy for camera/mic.

OS hardening (CIS-aligned)

For Linux hosts (CIS Ubuntu 22.04 LTS guidance):

  • Disable password SSH auth; use keys.

  • Enable automatic security updates.

  • Restrict outbound egress where possible (Twilio endpoints + your storage).

  • systemd hardening (see unit file: NoNewPrivileges , ProtectSystem=strict , etc.).

Abuse prevention

  • Rate limit token endpoint by:

  • user id

  • IP

  • room id

  • Require authenticated session; never mint tokens anonymously.

  • Validate room membership server-side; do not trust client-provided room names.

Performance Tuning

Reduce API calls (measurable)

Before: polling room participants every 2 seconds per active room.

  • 500 rooms → 250 req/s → triggers 20429.

After: event-driven + cached state:

  • Poll only on state transitions (room created/completed) or on-demand admin actions.

  • Expected reduction: >90% API traffic.

Implementation:

  • Cache room metadata in Redis with TTL.

  • Use client-side events for participant join/leave; send to backend via your own websocket if needed.

Bandwidth profile tuning

Set maxSubscriptionBitrate to cap downstream:

  • Default (uncapped): can exceed 4–6 Mbps in grid views.

  • Tuned: 2.5 Mbps cap for collaboration.

  • Expected impact: fewer freezes on mid-tier connections; lower egress cost.

Track priority for presenter

  • Set presenter video to high , others standard/low .

  • Expected impact: presenter remains stable under congestion; non-critical tiles degrade first.

Composition cost control

  • Only create compositions when needed:

  • user requested recording

  • compliance requirement

  • Avoid composing every room by default.

Client-side capture constraints

Use 720p for presenter, 360p for others:

const localVideoTrack = await Video.createLocalVideoTrack({ width: 1280, height: 720, frameRate: 24 });

Expected impact:

  • Lower CPU on clients vs 1080p

  • Better stability on integrated GPUs

Advanced Topics

Multi-region considerations

  • Twilio Video media region selection may affect latency.

  • Keep your token service region-agnostic but consider routing users to nearest region via room configuration (where supported).

  • Measure RTT and join time by geography; store metrics per ISP.

Key rotation without downtime

Pattern:

  • Maintain ACTIVE_SIGNING_KEY_SID and NEXT_SIGNING_KEY_SID .

  • Mint tokens with active key; accept tokens signed by either during rotation window.

  • Rotate:

  • Create new key

  • Deploy config with both keys

  • Switch active

  • Wait > max TTL

  • Revoke old key

Idempotent composition creation

If your “room completed” handler can run twice:

  • Use DB unique constraint on roomSid for composition job.

  • If composition already exists, skip creation.

Handling reconnect storms

When a network blip occurs, many clients reconnect simultaneously.

Mitigations:

  • Token endpoint rate limiting with burst allowance

  • Client backoff before reconnect

  • Avoid creating new rooms on reconnect; reuse same room name

Safari/iOS constraints

  • Autoplay restrictions: require user gesture before starting audio.

  • Camera switching behavior differs; test with iOS 17+.

  • H.264 often more reliable on Safari; validate codec negotiation.

Data tracks for control plane

Use data tracks for:

  • “raise hand”

  • “mute request”

  • “layout hints”

But do not rely on them for authoritative moderation; enforce server-side policy.

Usage Examples

  1. Create a scheduled group room, mint tokens, and enforce max participants

Steps:

  • Backend creates room at schedule time.

  • Users request token; backend checks enrollment.

  • Client connects with bandwidth profile.

Backend (Node) create room:

const room = await client.video.v1.rooms.create({ uniqueName: "acme-prod:webinar:2026-02-21T18-00Z", type: "group", maxParticipants: 100, recordParticipantsOnConnect: false });

Token endpoint returns JWT scoped to room:

const jwt = mintVideoToken({ identity: "tenant_acme:user_42", room: "acme-prod:webinar:2026-02-21T18-00Z", ttlSeconds: 1800 });

  1. Moderator disconnects abusive participant

Admin UI calls backend:

curl -X POST https://video.acme.com/admin/rooms/RM.../participants/PA.../disconnect

Backend:

await client.video.v1.rooms(roomSid).participants(participantSid) .update({ status: "disconnected" });

Audit log:

  • actor identity

  • participant identity

  • timestamp

  • reason code

  1. Post-session composition + SMS notification (Messaging cluster pattern)

Flow:

  • Room completed.

  • Create composition.

  • On callback completed , store media URL, send SMS.

Composition callback handler:

  • Verify signature

  • If Status=completed , enqueue job send_recording_sms

SMS send (ensure STOP handling + status callbacks in your messaging service).

  1. Network quality-driven UI downgrade to audio-only

Client:

  • Subscribe to networkQualityLevelChanged .

  • If level <= 1 for > 10 seconds:

  • disable local video track

  • show banner

  • When level >= 3 for > 20 seconds:

  • re-enable video

Pseudo:

let lowSince = null;

room.on("networkQualityLevelChanged", (level) => { const now = Date.now(); if (level <= 1) { lowSince ??= now; if (now - lowSince > 10000) { room.localParticipant.videoTracks.forEach(pub => pub.track.disable()); } } else { lowSince = null; } });

  1. Key rotation drill (monthly)
  • Create new API key.

  • Deploy token service with both secrets.

  • Switch active signing key.

  • Validate new tokens connect.

  • After 2 hours (if TTL=3600), revoke old key.

Commands:

twilio api:core:keys:create --friendly-name "video-prod-2026-03" -o json twilio api:core:keys:list -o table --properties sid,friendlyName,dateCreated twilio api:core:keys:remove --sid SKoldkey...

  1. Incident: 20429 rate limit during peak

Mitigation steps:

  • Immediately increase backoff in API callers.

  • Disable non-essential polling endpoints.

  • Use cached room state for dashboards.

  • Postmortem: identify hot loops (participants:list) and replace with event ingestion.

Quick Reference

Task Command / API Key flags / fields

List in-progress rooms twilio api:video:v1:rooms:list

--status in-progress --limit 50 -o table

Create room twilio api:video:v1:rooms:create

--unique-name ... --type group --max-participants N

Complete room twilio api:video:v1:rooms:update

--sid RM... --status completed

List participants twilio api:video:v1:rooms:participants:list

--room-sid RM... --status connected

Disconnect participant twilio api:video:v1:rooms:participants:update

--room-sid RM... --sid PA... --status disconnected

Create composition twilio api:video:v1:compositions:create

--room-sid RM... --audio-sources "*" --format mp4 --status-callback URL

Fetch composition twilio api:video:v1:compositions:fetch

--sid CJ...

Rotate API key twilio api:core:keys:create/remove

--friendly-name ... / --sid SK...

Mint token (Node) AccessToken + VideoGrant

identity , ttl , room

Graph Relationships

DEPENDS_ON

  • twilio-core-auth : Account SID, API Key/Secret, Auth Token handling, key rotation

  • twilio-webhooks : signature validation, retry/idempotency patterns

  • webrtc-client : browser/device constraints, ICE/TURN behavior, media permissions

  • observability : structured logs, request IDs, metrics, alerting

COMPOSES

  • twilio-messaging : SMS/WhatsApp notifications for session events, recording delivery, status callbacks, STOP handling

  • twilio-verify : step-up authentication before token minting / recording access

  • sendgrid-email : transactional email for recording links, audit trails, bounce handling

  • twilio-studio : orchestration of reminders, surveys, escalation flows

  • twilio-voice : PSTN fallback for audio when WebRTC fails; IVR for support

SIMILAR_TO

  • zoom-video-sdk (conceptually similar: rooms/sessions, tokens, recording)

  • agora-rtc (tracks, channel join, bandwidth profiles)

  • daily-webrtc (rooms, recording, webhooks)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

playwright-scraper

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

clawflows

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

tavily-web-search

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

humanize-ai-text

No summary provided by upstream source.

Repository SourceNeeds Review