6.6 KiB
6.6 KiB
RTMS - Quickstart
Get started with Zoom Realtime Media Streams.
Prerequisites
- Node.js 20.3.0+ (24 LTS recommended)
- Zoom General App (for meetings/webinars), Video SDK App (for Video SDK), or approved Contact Center / RTMS integration for Zoom Contact Center Voice
- Webhook endpoint configured
- Server to handle WebSocket connections
Environment Setup
# .env file
ZOOM_CLIENT_ID=your_client_id # From App Credentials
ZOOM_CLIENT_SECRET=your_client_secret # From App Credentials
ZOOM_SECRET_TOKEN=your_webhook_token # From Feature → Webhook
Option 1: RTMSManager (Recommended)
Clone the official samples and use RTMSManager:
git clone https://github.com/zoom/rtms-samples.git
cd rtms-samples/boilerplate/working_js
npm install
import { RTMSManager } from './library/javascript/rtmsManager/RTMSManager.js';
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
// Initialize with credentials
await RTMSManager.init({
credentials: {
meeting: {
clientId: process.env.ZOOM_CLIENT_ID,
clientSecret: process.env.ZOOM_CLIENT_SECRET,
secretToken: process.env.ZOOM_SECRET_TOKEN,
}
},
// Use bitmask: AUDIO(1) | TRANSCRIPT(8) = 9
mediaTypes: RTMSManager.MEDIA.AUDIO | RTMSManager.MEDIA.TRANSCRIPT,
logging: 'info'
});
// Handle media events
RTMSManager.on('audio', ({ buffer, userName }) => {
console.log(`Audio from ${userName}: ${buffer.length} bytes`);
});
RTMSManager.on('transcript', ({ text, userName }) => {
console.log(`${userName}: ${text}`);
});
RTMSManager.on('chat', ({ text, userName }) => {
console.log(`[Chat] ${userName}: ${text}`);
});
RTMSManager.on('sharescreen', ({ buffer, userName }) => {
console.log(`Screen share from ${userName}`);
});
// CRITICAL: Respond 200 IMMEDIATELY before any processing!
app.post('/webhook', (req, res) => {
res.status(200).send(); // FIRST!
const { event, payload } = req.body;
// Handle URL validation challenge
if (event === 'endpoint.url_validation') {
const hash = crypto
.createHmac('sha256', process.env.ZOOM_SECRET_TOKEN)
.update(payload.plainToken)
.digest('hex');
return res.json({ plainToken: payload.plainToken, encryptedToken: hash });
}
// Feed RTMS events to manager
RTMSManager.handleEvent(event, payload);
});
await RTMSManager.start();
app.listen(3000, () => console.log('RTMS server on port 3000'));
Option 2: @zoom/rtms SDK
npm install @zoom/rtms express
import rtms from "@zoom/rtms";
const RTMS_EVENTS = ["meeting.rtms_started", "webinar.rtms_started", "session.rtms_started"];
rtms.onWebhookEvent(({ event, payload }) => {
if (!RTMS_EVENTS.includes(event)) return;
const client = new rtms.Client();
client.onAudioData((data, timestamp, metadata) => {
console.log(`Audio from ${metadata.userName}`);
});
client.onTranscriptData((data, timestamp, metadata) => {
console.log(`${metadata.userName}: ${data}`);
});
client.onChatData((data, timestamp, metadata) => {
console.log(`[Chat] ${metadata.userName}: ${data}`);
});
client.onScreenShareData((data, timestamp, metadata) => {
console.log(`Screen share from ${metadata.userName}`);
});
// SDK accepts both meeting_uuid and session_id transparently
client.join(payload);
});
Zoom App Setup Steps
For Meetings and Webinars (General App)
- Go to marketplace.zoom.us
- Click Develop → Build App
- Choose General App → User-Managed
- Features → Access → Enable Event Subscription
- Add Events → Search "rtms" → Select RTMS endpoints:
meeting.rtms_startedmeeting.rtms_stoppedwebinar.rtms_started(if using webinars)webinar.rtms_stopped(if using webinars)
- Scopes → Add Scopes → Search "rtms" → Add:
meeting:read:meeting_audiomeeting:read:meeting_videomeeting:read:meeting_transcriptmeeting:read:meeting_chatwebinar:read:webinar_audio(if using webinars)webinar:read:webinar_video(if using webinars)webinar:read:webinar_transcript(if using webinars)webinar:read:webinar_chat(if using webinars)
For Video SDK (Video SDK App)
- Go to marketplace.zoom.us
- Click Develop → Build App
- Choose Video SDK App
- Add Events:
session.rtms_startedsession.rtms_stopped
- Use SDK Key and SDK Secret for authentication (not OAuth credentials)
How to Start RTMS
RTMS must be started for each meeting/webinar/session. Options:
| Product | How to Start | Webhook Event |
|---|---|---|
| Meeting | Zoom client, REST API, Zoom App SDK, or autostart (zoom.us settings) | meeting.rtms_started |
| Webinar | Zoom client, REST API, Zoom App SDK, or autostart (zoom.us settings) | webinar.rtms_started |
| Video SDK | Video SDK client or REST API | session.rtms_started |
| Zoom Contact Center Voice | Product-specific RTMS/ZCC Voice flow | Product-specific RTMS/ZCC Voice events |
Webinar note: Panelists have full audio/video streams. Attendee streams may not be available individually.
March 2026 note: transcript handshakes now support
src_languageplusenable_lid, media socket keep-alive tolerance is now about 65s, and RTMS supports one selected participant camera stream at a time viaVIDEO_SINGLE_INDIVIDUAL_STREAM+VIDEO_SUBSCRIPTION_REQ.
Deployment with Reverse Proxy
When deploying behind nginx with a path prefix (e.g., /my-app/):
- Socket.IO path must match nginx config:
const socketPath = window.location.pathname.includes('/my-app')
? '/my-app/rtms-socket'
: '/rtms-socket';
const socket = io({ path: socketPath });
- API calls must use relative paths:
const basePath = window.location.pathname.replace(/\/$/, '');
fetch(`${basePath}/api/sessions`); // NOT fetch('/api/sessions')
- Nginx WebSocket proxy:
location /my-app/rtms-socket {
proxy_pass http://YOUR_RTMS_BACKEND_HOST:3000/rtms-socket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Next Steps
- Media Types - All 5 data types (audio, video, transcript, chat, screen share)
- Connection - WebSocket protocol & message types
- Webhooks - Event subscription details
Resources
- RTMS docs: https://developers.zoom.us/docs/rtms/
- rtms-samples: https://github.com/zoom/rtms-samples