Skip to main content
Back to blog

Migrating from Twilio Video to Devotel Orbit RTC — SDK mapping and cutover checklist

Twilio Video's full sunset lands by the end of 2026. This is the code-level runbook — Twilio Room/Track/publish concepts mapped to Orbit's room, join-token and grant surface, recording and webhook parity, and a staged cutover that keeps receivers alive while you switch.

Orbit Editorial Team

Quick answer: Twilio Video — Twilio Programmable Video, the WebRTC room SDK — is announced to fully sunset by the end of 2026, so a "migrate the video SDK" project is now a dated work-stream, not an optional evaluation. The code-level move to Devotel Orbit RTC is bounded: Twilio's Room → Participant → Track → publish/subscribe surface maps onto Orbit's room object, server-minted join tokens, effective-grant response, and the prebuilt orbit-video-room embed. Recording callbacks re-point onto Orbit's video.* webhook events, and the cutover runs as a staged dual-publish window rather than a flag-day. The umbrella frame for every Twilio family is the vendor deprecation runbook — this post is the code-level SDK runbook the umbrella only gestures at. For the voice and SMS families the migration from Twilio guide already walks the same sequence.

If you run Twilio Video today, the question this runbook answers is concrete: which lines of my SDK integration move where, what replaces the access-token exchange, and how do I cut receivers over without dropping a session mid-call?

1. Twilio Video's end-of-life: what the sunset actually retires

Twilio has announced a full product-line sunset for Twilio Video by the end of 2026 — the whole product retires, not an API version. Sorted into the deprecation classes from the umbrella runbook, this is the worst severity: a "move the family" event. What actually retires on the Twilio side:

  • The client SDKs. twilio-video.js and the iOS/Android Video SDKs stop receiving a backend — connect(token, { name }) has no room to join once the service goes read-only.
  • The REST surface. Room creation (POST /v1/Rooms), the composition/recording APIs, and the room status callback endpoints.
  • The token credential pair. The API Key/Secret that signs Video grant JWTs (VideoGrant) stops minting usable credentials.

What does not retire automatically is everything around your integration: the access-token exchange in your backend, your recording webhook consumer, and the per-participant permission logic you encoded in Twilio grant construction. Those are the parts a runbook ports — the SDK swap is the smallest piece of the move.

The budgeting point from the umbrella piece applies verbatim: a product-line sunset gets a work-stream with a replacement decision, and the replacement on Devotel Orbit is the video API — scheduled and ad-hoc in-browser rooms, server-minted join tokens, recording, moderation, and broadcast, on the same account as your voice and messaging.

2. SDK surface mapping: Room, Participant, Track, publish/subscribe

Twilio Video's object model has four levels: Room (the session), Participant (local and remote), Track (audio/video/data publications), and the publish/subscribe operations a participant performs. Orbit's RTC surface covers the same model but consolidates it behind a server-minted token and a prebuilt element, so the client code shrinks:

Twilio Video conceptDevotel Orbit equivalent
Twilio.Video.connect(token, { name })RoomCreate the room server-side once: POST /api/v1/video/rooms-scheduled (persistent, named) or POST /api/v1/video/rooms (ad-hoc); the room persists beyond any single session
LocalParticipant / RemoteParticipantA participant is an identity plus a grant tier bound into a join token — participant_tier of host, panelist, viewer, or hidden_supervisor
LocalTrack / RemoteTrack (publications)The effective grant in the join response — permissions.can_publish / permissions.can_subscribe — decides publish and subscribe per participant
localParticipant.publishTrack(track)Publish is implicit at connect: a grant with can_publish: true publishes camera/microphone when the user toggles them; a viewer grant is receive-only
room.on('participantConnected') / track eventsDOM events from the embed: orbit-participant-joined, orbit-participant-left, orbit-state, orbit-error — and server-side video.participant.joined / video.participant.left webhooks
room.disconnect()Drop the element or close the LiveKit client; an empty ad-hoc room is reaped promptly, a scheduled room returns to its scheduled state
Room.getStats()Live per-participant QoS telemetry delivered as the video.participant.qos webhook — telemetry moves server-side, outbound from your client

The most useful frame for the port: on Twilio you ship a client SDK that does everything in the browser against a grant JWT; on Orbit you mint a self-describing join token server-side and hand it to either the prebuilt orbit-video-room element (the same landing-style embed from the web SDK) or a LiveKit client SDK for a custom UI. A Twilio integration that built its own tile/grid UI ports to the LiveKit client path; an integration that used Twilio's defaults ports to the embed and deletes the UI code.

<!-- Twilio: twilio-video.js + your own tile layout + attach logic -->
<!-- Orbit: the prebuilt room element -->
<script type="module"
  src="https://cdn.jsdelivr.net/npm/@devotel-orbit/web@latest/dist/index.mjs">
</script>

<div style="height:600px">
  <orbit-video-room
    token="SERVER_MINTED_JOIN_TOKEN"
    server-url="ws-url-from-join-response"
    display-name="Support agent"
    enable-screen-share="true"
  ></orbit-video-room>
</div>

3. Token-generation parity: TTL, room access, per-participant grants

Twilio's token flow builds an AccessToken with a VideoGrant (room name) and optional per-participant constraints, signed by an API Key/Secret pair, TTL'd with ttl. Orbit's parity surface is the join endpoint — the browser never sees your API key, and every knob Twilio encoded into grant construction moves into the join request body:

import { Orbit } from "@devotel-orbit/node";

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

// Twilio: new AccessToken(accountSid, apiKey, secret, { ttl: 3600 })
//   token.addGrant(new VideoGrant({ room: 'consult-4821' }));
// Orbit: POST /api/v1/video/rooms-scheduled/:id/join via the escape hatch.
const join = await orbit.request(
  "POST",
  "/video/rooms-scheduled/room_01HZYJ6W2K/join",
  {
    participant_name: "guest",
    ttl_seconds: 3600, // same TTL knob as AccessToken's `ttl`, in seconds
  },
);

console.log(join.data.token);             // livekit-access JWT for the client
console.log(join.data.participant_tier);  // "panelist" — the grant tier bound at mint
console.log(join.data.permissions);       // effective grant — read this, never assume
console.log(join.data.expires_in_seconds);

The parity table, knob by knob:

  • TTL. Twilio's AccessToken ttl moves to ttl_seconds on the join body (default one hour). Re-issuing is a fresh POST every time — mint a new token when expires_at approaches instead of holding a long-lived JWT.
  • Room access. Twilio's VideoGrant({ room }) moves to the endpoint path itself — the token is minted against a room id, so "which room" is enforced by the resource you called, not by a claim inside the JWT.
  • Per-participant grants. Twilio's ad-hoc grant construction moves to Orbit's grant tiers — host, panelist, viewer, hidden_supervisor — with the effective grant echoed back in permissions (can_publish, can_subscribe, can_publish_data, hidden, room_admin). Port your permission matrix by reading the response, not by assuming the requested tier was granted.
  • Identity binding. Twilio tokens carry an identity claim loosely enforced by your backend. On Orbit, identity binds at mint time and minting a token under another user's identity requires the owner or admin role plus a non-empty reason — the spoofing hole an ordinary API key had on Twilio is closed by design.

The one behavioral difference to plan for: because join tokens are per-participant and short-lived, the token-vending endpoint in your backend stays — but it becomes a thin proxy to the join API instead of a local JWT signer. If your current exchange derives permissions from user records, keep that logic; only the signing step moves.

4. Recording and the webhook shim: Twilio callbacks to Orbit event shapes

Twilio Video delivers lifecycle and recording events as room status callbacks and recording callbacks (POSTs with StatusCallbackEvent values like participant-connected, and recording events when a composition completes). Orbit delivers the same lifecycle as channel webhooks on the standard envelope — register your endpoint once and map the event names:

Twilio Video callbackDevotel Orbit webhook
room-createdvideo.room.started
room-endedvideo.room.ended
participant-connected / participant-disconnectedvideo.participant.joined / video.participant.left
recording-completed (composition finished)video.recording.completed
recording-failedvideo.recording.failed
track-enabled / track-disabled (moderator-audit use)video.participant.muted / video.participant.unmuted
— (no Twilio counterpart)video.recording.transcript_ready (post-meeting transcription of a recording completes)

The shim is a rename layer in front of your existing consumer, not a rewrite: keep the Twilio event names as your internal vocabulary for one release, translate incoming Orbit events into them at the edge, then rename downstream once the cutover lands. Two parity differences worth writing down in the shim's README:

  • Recording consent is per-participant. Twilio records the whole room or nothing. On Orbit, a participant can decline at join time (allow_recording in the join body) and a host can change consent mid-room; a decliner's media is excluded from the composite and per-track recordings. If your compliance program promised "recording on or off for the room," say so in the join flow rather than discovering per-participant exclusion after go-live.
  • Recording links are short-lived by design. Twilio compositions land on Twilio storage with account credentials. Orbit returns a one-hour signed URL on the room record (recording_url with recording_url_expires_at) — fetch the room again when the link lapses, or export finished recordings to your own storage and treat Orbit's link as a transfer window, not an archive.

5. The staged cutover checklist: dual-publish, switch receivers, parity telemetry, decommission

A video migration fails in the phase between "works in staging" and "old vendor turned off." Run it as five gates, each with a rollback state you can name:

  1. Freeze the inventory. List every flow that calls Twilio.Video.connect: support consultations, telehealth visits, embedded demos. Each becomes a row with its room-creation site, token-vending endpoint, callback consumer, and recording destination. Gate exit: every row names an Orbit room kind (scheduled or ad-hoc) and a grant tier per participant class.
  2. Dual-publish one flow. Port one low-risk flow end-to-end while Twilio stays live for the rest: create the Orbit room alongside the Twilio room (both created server-side; the Orbit room is the shadow), mint Orbit join tokens for internal users, and run real sessions with your own team. Gate exit: recording lands on Orbit, webhooks fire into the shim, QoS telemetry (video.participant.qos) matches your Twilio-era baseline for the same route.
  3. Switch the receivers. "Receivers" are whoever joins last: customers, patients, guests. Move the guest-join link to the Orbit room while agent-side stays on the shadow Twilio room for the rollback window — the dual-publish episode means your team can fall back by reverting the link. Gate exit: receiver sessions complete on Orbit with no Twilio-side join in the last N days, and the rollback path is still warm.
  4. Reach telemetry parity. Before you decommission anything, prove the observability you had survives: join/leave webhooks feeding session analytics, QoS telemetry feeding quality alerts, video.recording.degraded verdicts feeding recording QC, and the moderation events (video.participant.muted/kicked/banned) feeding your audit log. Gate exit: one full business week with parity dashboards green on both vendors.
  5. Decommission Twilio. Delete the Twilio room-creation and token paths, remove twilio-video.js from the bundle, revoke the API Key/Secret pair, and cancel the recording callback URLs — then keep the webhook shim translating event names until the downstream rename ships on its own schedule. Gate exit: the Twilio Video product hits its sunset date as a no-op for you.

A note on tenant-owned controls before go-live (they belong in gate 1's checklist, not as an afterthought): recording consent capture, session-history retention windows, and the participant cap are tenant-configurable on Orbit — set them on your own account as part of the rollout, and keep the Orbit vs Twilio Video head-to-head cells for the pricing and capability rows a buyer validates against.

Frequently asked questions

Does Orbit's video API cover what Twilio Video's SDK did?

Yes for the shipped core: in-browser rooms, per-participant publish/subscribe grants, screen share, recording, live broadcast, and a prebuilt embed — parity the comparison registry credits to both products. Orbit then adds the shape Twilio Video never had on one account: agent co-browse, AI video avatar participants, and the voice/SMS/email channels a video session usually travels with.

What replaces Twilio's AccessToken + VideoGrant flow?

A server-side call to the join endpoint — POST /api/v1/video/rooms-scheduled/:id/join (or the ad-hoc variant) — with participant_name, an optional ttl_seconds, and the grant tier. The response is self-describing: the token, the SFU websocket URL, the effective permissions grant, and the expiry, so your client never decodes a JWT.

How do Twilio Video's recording callbacks map to Orbit?

Room and participant lifecycle events arrive as video.room.started/video.room.ended/video.participant.joined/video.participant.left, and recording completion as video.recording.completed / video.recording.failed, on the standard webhook envelope. Keep your Twilio event names as an internal vocabulary for one release and translate at the edge; Orbit adds per-participant recording consent and short-lived signed recording URLs, which the cutover checklist treats explicitly.

Can we run Twilio Video and Orbit side by side during the cutover?

Yes — that is the recommended shape. Create the Orbit room as a shadow alongside the Twilio room for one flow (dual-publish), move the receiver join link over while the rollback path stays warm, and hold decommission until join/leave webhooks, QoS telemetry, and recording QC run at parity for a full business week.

The takeaway

Twilio Video sunsets as a whole product by the end of 2026, and the code-level move is a bounded port: map Room → Participant → Track → publish/subscribe onto Orbit's room, join token, and effective grant; move the TTL/room/grant knobs into the join request; shim the recording callbacks onto the video.* webhook events; and cut over as dual-publish → receiver switch → telemetry parity → decommission. The umbrella frame for the other Twilio families lives in the vendor deprecation runbook, and the head-to-head capability matrix sits on the Orbit vs Twilio Video comparison.

Migrating from Twilio Video to Devotel Orbit RTC — SDK mapping and cutover checklist — Orbit by Devotel