Migration Guide

Twilio Video narrowed its focus.
Here's why teams are migrating.

Twilio announced a sunset of Programmable Video in early 2024, then reversed that decision in October 2024 — but narrowed its focus to 1:1 customer engagement verticals (telehealth, financial services, retail support). Teams building group conferencing, multi-tenant platforms, or custom RTC workflows are looking for an exit. TomatoRTC covers the core Twilio Video surface area — multi-party rooms, signaling, TURN relay, per-stream recording — on infrastructure you own.

Multi-party SFU rooms Browser JS SDK TURN relay Per-stream recording Self-hostable

Day one

Full parity for the Twilio Video use case — on infrastructure you own.

No per-minute billing. No vendor lock-in on the media path. You migrate to a platform you control.

Multi-party SFU rooms Shipped

Production SFU path — equivalent to Twilio Group Rooms.

Peer-to-peer mesh Shipped

Browser-to-browser topology — equivalent to Twilio P2P rooms.

Browser JS SDK Shipped

connect / publish / subscribe API surface with similar ergonomics.

TURN relay Shipped

First-party, tenant-scoped, short-lived credentials. In-repo, not a third party.

API mapping

Access Token → standard JWT you sign.

No Twilio account credentials. No Twilio SDK dependency on the server. You control the secret; TomatoRTC validates your tokens.

  • tenantId required Enforced across all room and admin operations — scopes every call.
  • role claim host | participant | observer carried in the token.
  • Your secret Sign with any standard JWT library. No Twilio dependency.
server/token.ts ts
// Before: Twilio AccessToken + VideoGrant
const token = new AccessToken(accountSid, keySid, keySecret,
  { identity: 'alice' });
token.addGrant(new VideoGrant({ room: 'my-room' }));

// After: standard JWT signed with your own secret
import jwt from 'jsonwebtoken';
const token = jwt.sign(
  {
    sub: 'alice',
    tenantId: 'my-tenant', // ← required
    role: 'participant',  // ← host | participant | observer
  },
  process.env.JWT_SECRET,  // ← your secret
  { expiresIn: '1h', audience: 'tomatortc' }
);
res.json({ token });

Room creation

Same REST pattern, your server, your topology.

Rooms are created via admin API or auto-created on join. The topology field selects SFU (Group Room equivalent) or mesh (P2P equivalent).

REST — room creation bash
# Before: Twilio REST API
curl -X POST https://video.twilio.com/v1/Rooms \
  -u "$ACCOUNT_SID:$AUTH_TOKEN" \
  -d "UniqueName=my-room" \
  -d "Type=group"

# After: TomatoRTC REST API
curl -X POST https://your-server/api/rooms \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "roomId": "my-room", "tenantId": "my-tenant",
     "topology": "sfu" }'

# topology: "sfu" = Group Room | "mesh" = P2P room
# RTC_AUTO_CREATE_ROOMS=true skips explicit creation

Browser SDK

One connect call, native MediaStreamTrack — no Twilio wrapper objects.

TomatoRTC uses native MediaStreamTrack directly. No Twilio SDK dependency on the client. Convenience methods replace low-level track creation.

client/room.ts ts
// Before: twilio-video
import { connect } from 'twilio-video';
const room = await connect(token,
  { name: 'my-room', audio: true, video: true });
await room.localParticipant.publishTrack(localVideoTrack);

// After: @rtc-sdk/client-browser
import { createRtcClient } from '@rtc-sdk/client-browser';
const client = createRtcClient({
  signalingUrl: 'wss://your-server',
  authToken: token,
  displayName: 'Alice',
});
const room = await client.joinRoom('my-room');
await room.publishCamera();      // convenience
await room.publishMicrophone();  // convenience
// or: room.publishTrack(mediaStreamTrack, { kind: 'video' })

Event mapping

Every Twilio event has a direct equivalent.

Track attachment is unchanged: videoElement.srcObject = new MediaStream([track])

Twilio Event
TomatoRTC Equivalent
Room.participantConnected
room.on('participantJoined', ...)
Room.participantDisconnected
room.on('participantLeft', ...)
Participant.trackSubscribed
room.on('trackSubscribed', ...)
Participant.trackUnsubscribed
room.on('trackUnsubscribed', ...)
Room.disconnected
room.on('disconnected', ...)
LocalTrack.stopped
room.on('localTrackEnded', ...)
Room.reconnecting
room.on('reconnecting', ...)
Room.reconnected
room.on('reconnected', ...)

Track publication

Native MediaStreamTrack — drop the Twilio LocalTrack wrapper.

Twilio's LocalTrack objects are not compatible with TomatoRTC. Use native MediaStreamTrack directly or call convenience methods.

publishCamera()

Captures and publishes the default video device. Returns a track handle.

publishMicrophone()

Captures and publishes the default audio device.

publishScreen()

Launches getDisplayMedia() and publishes the screenshare track.

publishTrack(track, options)

Publish any existing MediaStreamTrack. Pass { kind: "video" | "audio" }.

Recording

Synthetic WebM foundation today. Bounded PMG compositor slices alongside it.

TomatoRTC ships recording contracts, VP8/Opus WebM muxing, and an in-process synthetic SFU demo. Live signaling-room worker orchestration is not yet a stock recording path; PMG compositor scenes are a separate bounded output capability.

  • Lifecycle API Recording lifecycle contracts and management endpoints exist; stock live-room worker wiring remains hardening work.
  • Output proof The recording demo writes per-producer VP8/Opus WebM artifacts from synthetic SFU RTP.
  • Composition Bounded batch/live PMG scenes ship separately; a durable room-controlled composed-recording workflow remains future work.

Migration steps

Seven steps from Twilio Video to TomatoRTC.

1

Stand up TomatoRTC locally

pnpm install && pnpm dev:server:sfu:https — verify at https://localhost:8443/kitchen-sink/ (~15 min)

2

Swap the token endpoint

Replace AccessToken + VideoGrant with a standard JWT signed with your secret. Pass it to createRtcClient.

3

Replace the room connect call

twilio-video connect() → createRtcClient().joinRoom(). Update event names per the mapping table.

4

Replace track publication

room.localParticipant.publishTrack() → room.publishCamera() / publishMicrophone() / publishTrack().

5

Update participant and track rendering

Participant.tracks map → room.remoteParticipants + trackSubscribed event. Track attachment unchanged.

6

Migrate recording

Treat live-room recording as an integration project: validate worker wiring, storage, synchronization, and whether bounded PMG scenes fit the output requirement.

7

Update environment and deploy

Set signalingUrl, JWT_SECRET, TURN credentials, and topology in .env. Deploy via Kubernetes manifests or deploy.cloud.sh.

Known gaps

Honest status of every Twilio Video feature.

Most gaps are engineering priorities, not blockers. Factor mobile and composited recording into your timeline if you depend on them.

Twilio Feature
TomatoRTC Status
Group Rooms (multi-party SFU)
✓ Shipped
Peer-to-peer rooms
✓ Shipped
Dominant speaker detection
In hardening
Network bandwidth profile
Simulcast shipped; client BWE profile: proposed
Recording (per-stream)
✓ Foundation shipped
Recording (composited / layout)
Bounded PMG slices; durable recording workflow open
iOS / Android native SDK
Signaling foundation; native media interop hardening
PSTN / SIP interop
Deliberately deferred

Migration SOW

Fixed-scope migration engagement for deadline-driven teams.

For teams migrating from Twilio Video under a deadline, TomatoRTC offers a scoped SOW. Contact sales for scoping: sales@tomatortc.com

Architecture review

Map your Twilio room topology and recording path to TomatoRTC equivalents.

Token and room API integration

Swap AccessToken generation, validate JWT flow end-to-end.

Client SDK swap

Event handler migration, track publication, remote participant rendering.

Recording path planning

Live worker integration, storage, and bounded PMG compositor fit assessment.

Pre-launch load testing

Verify SFU room scale and TURN relay capacity before go-live.

$40,000 – $120,000 depending on complexity

Next steps

Three actions to start your migration.

  1. Run the kitchen-sink demo locally

    Stand up the full platform in minutes — verify signaling, SFU, and TURN work in your environment before committing to the migration.

  2. Map your event handlers

    The event table on slide 6 covers every Twilio Video event you likely use. Estimate the client-side effort.

  3. Schedule a scoping call

    We will scope the migration, confirm gap impact for your use case, and define a fixed-fee SOW if needed.