Zoom Realtime Media Streams (RTMS)
Expert guidance for accessing live audio, video, transcript, chat, and screen share data from Zoom meetings, webinars, Video SDK sessions, and Zoom Contact Center Voice in real-time. RTMS uses a WebSocket-based protocol with open standards and does not require a meeting bot to capture the media plane.
Read This First (Critical)
RTMS is primarily a backend media ingestion service.
- Your backend receives and processes live media: audio, video, screen share, chat, transcript.
- RTMS is not a frontend UI SDK by itself.
- Processing is event-triggered: backend waits for RTMS start webhook events before stream handling begins.
Optional architecture (common):
- Add a Zoom App SDK frontend for in-client UI/controls.
- Stream backend RTMS outputs to frontend via WebSocket (or SSE, gRPC, queue workers, etc.).
Use RTMS for media/data plane, and use frontend frameworks/Zoom Apps for presentation + user interactions.
Official Documentation: https://developers.zoom.us/docs/rtms/
SDK Reference (JS): https://zoom.github.io/rtms/js/
SDK Reference (Python): https://zoom.github.io/rtms/py/
Sample Repository: https://github.com/zoom/rtms-samples
Quick Links
New to RTMS? Follow this path:
- Connection Architecture - Two-phase WebSocket design
- SDK Quickstart - Fastest way to receive media (recommended)
- Manual WebSocket - Full protocol control without SDK
- Media Types - Audio, video, transcript, chat, screen share
Complete Implementation:
- RTMS Bot - End-to-end bot implementation guide
Reference:
- Lifecycle Flow - Complete webhook-to-streaming flow
- Data Types - All enums and constants
- Webhooks - Event subscription details
- Environment Variables - credential modes and runtime knobs
- Quickstart Notes - Secondary quickstart guide
- Integrated Index - see the section below in this file
Having issues?
- Connection fails -> Common Issues
- Duplicate connections -> Webhook Gotchas
- No audio/video -> Media Configuration
- Start with preflight checks -> 5-Minute Runbook
Supported Products
| Product |
Webhook Event |
Payload ID |
App Type |
| Meetings |
meeting.rtms_started / meeting.rtms_stopped |
meeting_uuid |
User-managed General App |
| Webinars |
webinar.rtms_started / webinar.rtms_stopped |
meeting_uuid (same!) |
User-managed General App |
| Video SDK |
session.rtms_started / session.rtms_stopped |
session_id |
Video SDK App |
| Zoom Contact Center Voice |
contact_center.voice_rtms_started / contact_center.voice_rtms_stopped |
Product-specific stream/session identifiers |
General user/admin or S2S, based on scopes |
Once connected, the core signaling/media socket model is shared across products. Meetings, webinars, and Video SDK sessions use the familiar start/stop webhooks. Zoom Contact Center Voice adds its own RTMS/ZCC Voice event family and should be treated as the same transport model with product-specific event payloads.
RTMS Overview
RTMS is a data pipeline that gives your app access to live media from Zoom meetings, webinars, and Video SDK sessions without participant bots. Instead of having automated clients join meetings, use RTMS to collect media data directly from Zoom's infrastructure.
What RTMS Provides
| Media Type |
Format |
Use Cases |
| Audio |
PCM (L16), G.711, G.722, Opus |
Transcription, voice analysis, recording |
| Video |
H.264, JPG, PNG |
Recording, AI vision, thumbnails, active participant selection |
| Screen Share |
H.264, JPG, PNG |
Content capture, slide extraction |
| Transcript |
JSON text |
Meeting notes, search, compliance |
| Chat |
JSON text |
Archive, sentiment analysis |
Recent Protocol Changes
- Zoom Contact Center Voice support: RTMS now covers Contact Center Voice audio and transcript scenarios.
- Transcript Language Identification control: transcript media handshakes now support
src_language and enable_lid. Default behavior is LID enabled. Set enable_lid: false to force a fixed language.
- Single individual video stream subscription: RTMS can now stream one participant's camera feed at a time when
data_opt is set to VIDEO_SINGLE_INDIVIDUAL_STREAM.
- Graceful client-initiated shutdown: backends can send
STREAM_CLOSE_REQ over the signaling socket and wait for STREAM_CLOSE_RESP.
- Media keep-alive tolerance increased: media socket keep-alive timeout is now 65 seconds, not 35.
- Audio buffer control in signaling handshake: signaling handshakes now support optional
buffer_data. Default is true, which delivers up to 60 seconds of initial audio buffered while signaling/media sockets are established. For live streaming pipelines, set buffer_data: false to drop the startup backlog and start from live audio once connected.
- Media payload length field: audio, video, and screen share media data events can include
content.length, the original binary length before base64 encoding.
- ZCC Voice enum update:
CONFERENCE_PARTICIPANT_LEFT = 29 was added to RTMS_ZCC_VOICE_EVENT_TYPE.
Two Approaches
| Approach |
Best For |
Complexity |
SDK (@zoom/rtms) |
Most use cases |
Low - handles WebSocket complexity |
| Manual WebSocket |
Custom protocols, other languages |
High - full protocol implementation |
Prerequisites
- Node.js 20.3.0+ (24 LTS recommended) for JavaScript SDK
- Python 3.10+ for Python SDK
- User-managed General App (meetings/webinars), General or S2S app (Contact Center Voice), or Video SDK App (Video SDK) with RTMS enabled
- Webhook endpoint for RTMS events
- Server to receive WebSocket streams
Need to create/configure the Marketplace app first? Use
Marketplace app management before RTMS
implementation. It documents General App manifests, event subscription API behavior,
app-owned client_credentials, and RTMS event validation drift. Use the user-managed
Meeting/Webinar RTMS template,
or the General App or S2S Contact Center Voice templates in the
template selector.
Need RTMS access? Post in Zoom Developer Forum requesting RTMS access with your use case.
Quick Start (SDK - Recommended)
import rtms from "@zoom/rtms";
// All RTMS start/stop events across products
const RTMS_EVENTS = ["meeting.rtms_started", "webinar.rtms_started", "session.rtms_started"];
// Handle webhook events
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}: ${data.length} bytes`);
});
client.onTranscriptData((data, timestamp, metadata) => {
const text = data.toString('utf8');
console.log(`${metadata.userName}: ${text}`);
});
client.onJoinConfirm((reason) => {
console.log(`Joined session: ${reason}`);
});
// SDK handles all WebSocket connections automatically
// Accepts both meeting_uuid and session_id transparently
client.join(payload);
});
Quick Start (Manual WebSocket)
For full control or non-SDK languages, implement the two-phase WebSocket protocol:
const WebSocket = require('ws');
const crypto = require('crypto');
const RTMS_EVENTS = ['meeting.rtms_started', 'webinar.rtms_started', 'session.rtms_started'];
// 1. Generate signature
// For meetings/webinars: uses meeting_uuid. For Video SDK: uses session_id.
function generateSignature(clientId, idValue, streamId, clientSecret) {
const message = `${clientId},${idValue},${streamId}`;
return crypto.createHmac('sha256', clientSecret).update(message).digest('hex');
}
// 2. Handle webhook
app.post('/webhook', (req, res) => {
res.status(200).send(); // CRITICAL: Respond immediately!
const { event, payload } = req.body;
if (RTMS_EVENTS.includes(event)) {
connectToRTMS(payload);
}
});
// 3. Connect to signaling WebSocket
function connectToRTMS(payload) {
const { server_urls, rtms_stream_id } = payload;
// meeting_uuid for meetings/webinars, session_id for Video SDK
const idValue = payload.meeting_uuid || payload.session_id;
const signature = generateSignature(CLIENT_ID, idValue, rtms_stream_id, CLIENT_SECRET);
const signalingWs = new WebSocket(server_urls);
signalingWs.on('open', () => {
signalingWs.send(JSON.stringify({
msg_type: 1, // Handshake request
protocol_version: 1,
meeting_uuid: idValue,
rtms_stream_id,
signature,
buffer_data: false, // Use false for live streaming; true preserves startup audio
media_type: 9 // AUDIO(1) | TRANSCRIPT(8)
}));
});
// ... handle responses, connect to media WebSocket
}
See: Manual WebSocket Guide for complete implementation.
Media Type Bitmask
Combine types with bitwise OR:
| Type |
Value |
Description |
| Audio |
1 |
PCM audio samples |
| Video |
2 |
H.264/JPG video frames |
| Screen Share |
4 |
Separate from video! |
| Transcript |
8 |
Real-time speech-to-text |
| Chat |
16 |
In-meeting chat messages |
| All |
32 |
All media types |
Example: Audio + Transcript = 1 | 8 = 9
Critical Gotchas
| Issue |
Solution |
| Only 1 connection allowed |
New connections kick out existing ones. Track active sessions! |
| Respond 200 immediately |
If webhook delays, Zoom retries creating duplicate connections |
| Heartbeat mandatory |
Respond to msg_type 12 with msg_type 13, or connection dies |
| Reconnection is YOUR job |
RTMS doesn't auto-reconnect. Media keep-alive tolerance is now about 65s; signaling remains around 60s |
| Transcript language drift |
Use src_language plus enable_lid: false when you want fixed-language transcription instead of automatic language switching |
| Audio params must match the support matrix |
Do not mix arbitrary content_type, codec, sample_rate, channel, and send_rate. The media server rejects unsupported combinations |
| Single participant video only |
VIDEO_SINGLE_INDIVIDUAL_STREAM supports one participant at a time. A new VIDEO_SUBSCRIPTION_REQ overrides the previous selection |
| Graceful close is explicit now |
Use STREAM_CLOSE_REQ / STREAM_CLOSE_RESP when your backend wants to terminate the stream cleanly |
| RTMS manifest validation can drift |
Create-time validation can reject RTMS event names that PUT /manifest persists on an existing draft app. Confirm with GET /manifest after updates |
Environment Variables
SDK Environment Variables
# Required - Authentication
ZM_RTMS_CLIENT=your_client_id # Zoom OAuth Client ID
ZM_RTMS_SECRET=your_client_secret # Zoom OAuth Client Secret
# Optional - Webhook server
ZM_RTMS_PORT=8080 # Default: 8080
ZM_RTMS_PATH=/webhook # Default: /
# Optional - Logging
ZM_RTMS_LOG_LEVEL=info # error, warn, info, debug, trace
ZM_RTMS_LOG_FORMAT=progressive # progressive or json
ZM_RTMS_LOG_ENABLED=true
Manual Implementation Variables
ZOOM_CLIENT_ID=your_client_id
ZOOM_CLIENT_SECRET=your_client_secret
ZOOM_SECRET_TOKEN=your_webhook_token # For webhook validation
Zoom App Setup
For Meetings and Webinars (General App)
- Go to marketplace.zoom.us -> Develop -> Build App
- Choose General App -> User-Managed
- Features -> Access -> Enable Event Subscription
- Add Events -> Search "rtms" -> Select:
meeting.rtms_started
meeting.rtms_stopped
webinar.rtms_started (if using webinars)
webinar.rtms_stopped (if using webinars)
- Scopes -> Add Scopes -> Search "rtms" -> Add:
meeting:read:meeting_audio
meeting:read:meeting_video
meeting:read:meeting_transcript
meeting:read:meeting_chat
webinar: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 -> Develop -> Build App
- Choose Video SDK App
- Use your SDK Key and SDK Secret (not OAuth Client ID/Secret)
- Add Events:
session.rtms_started
session.rtms_stopped
Sample Repositories
Official Samples
AI Integration Samples
Complete Documentation
Concepts
- Connection Architecture - Two-phase WebSocket design
- Lifecycle Flow - Webhook to streaming flow
Examples
- SDK Quickstart - Using @zoom/rtms SDK
- Manual WebSocket - Raw protocol implementation
- RTMS Bot - Complete bot implementation guide
- AI Integration - Transcription and analysis patterns
References
- Media Types - Audio, video, transcript, chat, screen share
- Data Types - All enums and constants
- Connection - WebSocket protocol details
- Webhooks - Event subscription
Troubleshooting
- Common Issues - FAQ and solutions
Resources
Need help? Start with Integrated Index section below for complete navigation.
Integrated Index
This section was migrated from SKILL.md.
RTMS provides real-time access to live audio, video, transcript, chat, and screen share from Zoom meetings, webinars, and Video SDK sessions.
Critical Positioning
Treat RTMS as a backend service for receiving and processing media streams.
- Backend role: ingest audio/video/share/chat/transcript, run AI/analytics, persist/forward data.
- Optional frontend role: Zoom App SDK or web dashboard that consumes processed stream data from backend transport (WebSocket/SSE/other).
- Kickoff model: backend waits for RTMS start webhook events, then starts stream processing.
Do not model RTMS as a frontend-only SDK.
Quick Start Path
If you're new to RTMS, follow this order:
Run preflight checks first -> RUNBOOK.md
Understand the architecture -> concepts/connection-architecture.md
- Two-phase WebSocket: Signaling + Media
- Why RTMS doesn't use bots
Choose your approach -> SDK or Manual
- SDK (recommended): examples/sdk-quickstart.md
- Manual WebSocket: examples/manual-websocket.md
Understand the lifecycle -> concepts/lifecycle-flow.md
- Webhook -> Signaling -> Media -> Streaming
Configure media types -> references/media-types.md
- Audio, video, transcript, chat, screen share
Troubleshoot issues -> troubleshooting/common-issues.md
- Connection problems, duplicate webhooks, missing data
Documentation Structure
rtms/
├── SKILL.md # Main skill overview
├── SKILL.md # This file - navigation guide
│
├── concepts/ # Core architectural patterns
│ ├── connection-architecture.md # Two-phase WebSocket design
│ └── lifecycle-flow.md # Webhook to streaming flow
│
├── examples/ # Complete working code
│ ├── sdk-quickstart.md # Using @zoom/rtms SDK
│ ├── manual-websocket.md # Raw protocol implementation
│ ├── rtms-bot.md # Complete RTMS bot implementation
│ └── ai-integration.md # Transcription and analysis
│
├── references/ # Reference documentation
│ ├── media-types.md # Audio, video, transcript, chat, share
│ ├── data-types.md # All enums and constants
│ ├── connection.md # WebSocket protocol details
│ └── webhooks.md # Event subscription
│
└── troubleshooting/ # Problem solving guides
└── common-issues.md # FAQ and solutions
By Use Case
I want to get meeting transcripts
- SDK Quickstart - Fastest approach
- Media Types - Transcript configuration
- AI Integration - Whisper, Deepgram, AssemblyAI
I want to record meetings
- Media Types - Audio + Video configuration
- SDK Quickstart - Receiving media
- AI Integration - Gap-filled recording
I want to build an AI meeting assistant
- AI Integration - Complete patterns
- SDK Quickstart - Media ingestion
- Lifecycle Flow - Event handling
I want to build a complete RTMS bot
- RTMS Bot - Complete implementation guide
- Lifecycle Flow - Webhook to streaming flow
- Connection Architecture - Two-phase design
I need full protocol control
- Manual WebSocket - START HERE
- Connection Architecture - Two-phase design
- Data Types - All message types and enums
- Connection - Protocol details
I'm getting connection errors
- Common Issues - Diagnostic checklist
- Connection Architecture - Verify flow
- Webhooks - Validation and timing
I want to understand the architecture
- Connection Architecture - Two-phase WebSocket
- Lifecycle Flow - Complete flow diagram
- Data Types - Protocol constants
By Product
I'm building for Zoom Meetings
- Standard RTMS setup. Webhook event:
meeting.rtms_started. Uses General App with OAuth.
- Start with SDK Quickstart or Manual WebSocket.
I'm building for Zoom Webinars
- Same as meetings, but webhook event is
webinar.rtms_started. Payload still uses meeting_uuid (NOT webinar_uuid).
- Add webinar scopes and event subscriptions. See Webhooks.
- Only panelist streams are confirmed available. Attendee streams may not be individual.
I'm building for Zoom Video SDK
- Webhook event:
session.rtms_started. Payload uses session_id (NOT meeting_uuid).
- Requires a Video SDK App with SDK Key/Secret (not OAuth Client ID/Secret).
- Once connected, the protocol is identical to meetings.
- See Webhooks for payload details.
Key Documents
1. Connection Architecture (CRITICAL)
concepts/connection-architecture.md
RTMS uses two separate WebSocket connections:
- Signaling WebSocket: Authentication, control, heartbeats
- Media WebSocket: Actual audio/video/transcript data
2. SDK vs Manual (DECISION POINT)
examples/sdk-quickstart.md vs examples/manual-websocket.md
| SDK |
Manual |
| Handles WebSocket complexity |
Full protocol control |
| Automatic reconnection |
DIY reconnection |
| Less code |
More code |
| Best for most use cases |
Best for custom requirements |
3. Critical Gotchas (MOST COMMON ISSUES)
troubleshooting/common-issues.md
- Respond 200 immediately - Delayed webhook responses cause duplicates
- Only 1 connection per stream - New connections kick out existing
- Heartbeat required - Must respond to keep-alive or connection dies
- Track active sessions - Prevent duplicate join attempts
Key Learnings
Critical Discoveries:
Two-Phase WebSocket Design
- Signaling: Control plane (handshake, heartbeat, start/stop)
- Media: Data plane (audio, video, transcript, chat, share)
- See: Connection Architecture
Webhook Response Timing
- MUST respond 200 BEFORE any processing
- Delayed response -> Zoom retries -> duplicate connections
- See: Common Issues
Heartbeat is Mandatory
- Signaling: Receive msg_type 12, respond with msg_type 13
- Media: Same pattern
- Failure to respond = connection closed
- See: Connection
Signature Generation
- Format:
HMAC-SHA256(clientSecret, "clientId,meetingUuid,streamId")
- For Video SDK, use
session_id in place of meetingUuid
- Webinars still use
meeting_uuid (not webinar_uuid)
- Required for both signaling and media handshakes
- See: Manual WebSocket
Media Types are Bitmasks
- Audio=1, Video=2, Share=4, Transcript=8, Chat=16, All=32
- Combine with OR: Audio+Transcript = 1|8 = 9
- See: Media Types
Startup Audio Buffering is a Signaling Choice
buffer_data belongs in SIGNALING_HAND_SHAKE_REQ
- default
true can replay up to 60s of startup audio
- set
false for live streaming or live-only startup behavior
- See: Connection
Screen Share is SEPARATE from Video
- Different msg_type (16 vs 15)
- Different media flag (4 vs 2)
- Must subscribe separately
- See: Media Types
Quick Reference
"Connection fails"
-> Common Issues
"Duplicate connections"
-> Webhook timing
"No audio/video data"
-> Media Types - Check configuration
"How do I implement manually?"
-> Manual WebSocket
"What message types exist?"
-> Data Types
"How do I integrate AI?"
-> AI Integration
Document Version
Based on Zoom RTMS SDK v1.x and official documentation as of 2026.
Happy coding!
Remember: Start with SDK Quickstart for the fastest path, or Manual WebSocket if you need full control.
1---2name: zoom-rtms3description: |4 Zoom Realtime Media Streams (RTMS) for accessing live audio, video, transcript, chat, and5 screen share from Zoom meetings, webinars, Video SDK sessions, and Zoom Contact Center Voice.6 WebSocket-based protocol using open web standards. Use when building AI/ML applications, live7 transcription, recording, streaming, or real-time meeting/webinar/session/contact-center analysis.8triggers:9 - "real-time media"10 - "rtms"11 - "live audio stream"12 - "live video stream"13 - "meeting transcription"14 - "raw audio"15 - "raw video"16 - "websocket media"17 - "live transcript"18 - "streaming audio"19 - "streaming video"20 - "meeting bot media"21 - "contact center voice media"22 - "participant video on"23 - "participant video off"24 - "single individual video stream"25---2627# Zoom Realtime Media Streams (RTMS)2829Expert guidance for accessing live audio, video, transcript, chat, and screen share data from Zoom meetings, webinars, Video SDK sessions, and Zoom Contact Center Voice in real-time. RTMS uses a WebSocket-based protocol with open standards and does not require a meeting bot to capture the media plane.3031## Read This First (Critical)3233RTMS is primarily a **backend media ingestion service**.3435- Your backend receives and processes live media: **audio, video, screen share, chat, transcript**.36- RTMS is not a frontend UI SDK by itself.37- Processing is **event-triggered**: backend waits for RTMS start webhook events before stream handling begins.3839Optional architecture (common):4041- Add a **Zoom App SDK** frontend for in-client UI/controls.42- Stream backend RTMS outputs to frontend via **WebSocket** (or SSE, gRPC, queue workers, etc.).4344Use RTMS for media/data plane, and use frontend frameworks/Zoom Apps for presentation + user interactions.4546**Official Documentation**: https://developers.zoom.us/docs/rtms/47**SDK Reference (JS)**: https://zoom.github.io/rtms/js/48**SDK Reference (Python)**: https://zoom.github.io/rtms/py/49**Sample Repository**: https://github.com/zoom/rtms-samples5051## Quick Links5253**New to RTMS? Follow this path:**54551. **[Connection Architecture](concepts/connection-architecture.md)** - Two-phase WebSocket design562. **[SDK Quickstart](examples/sdk-quickstart.md)** - Fastest way to receive media (recommended)573. **[Manual WebSocket](examples/manual-websocket.md)** - Full protocol control without SDK584. **[Media Types](references/media-types.md)** - Audio, video, transcript, chat, screen share5960**Complete Implementation:**61- **[RTMS Bot](examples/rtms-bot.md)** - End-to-end bot implementation guide6263**Reference:**64- **[Lifecycle Flow](concepts/lifecycle-flow.md)** - Complete webhook-to-streaming flow65- **[Data Types](references/data-types.md)** - All enums and constants66- **[Webhooks](references/webhooks.md)** - Event subscription details67- **[Environment Variables](references/environment-variables.md)** - credential modes and runtime knobs68- **[Quickstart Notes](references/quickstart.md)** - Secondary quickstart guide69- **Integrated Index** - see the section below in this file7071**Having issues?**72- Connection fails -> [Common Issues](troubleshooting/common-issues.md)73- Duplicate connections -> [Webhook Gotchas](troubleshooting/common-issues.md#webhook-response-timing)74- No audio/video -> [Media Configuration](references/media-types.md)75- Start with preflight checks -> [5-Minute Runbook](RUNBOOK.md)7677## Supported Products7879| Product | Webhook Event | Payload ID | App Type |80|---------|--------------|------------|----------|81| **Meetings** | `meeting.rtms_started` / `meeting.rtms_stopped` | `meeting_uuid` | User-managed General App |82| **Webinars** | `webinar.rtms_started` / `webinar.rtms_stopped` | `meeting_uuid` (same!) | User-managed General App |83| **Video SDK** | `session.rtms_started` / `session.rtms_stopped` | `session_id` | Video SDK App |84| **Zoom Contact Center Voice** | `contact_center.voice_rtms_started` / `contact_center.voice_rtms_stopped` | Product-specific stream/session identifiers | General user/admin or S2S, based on scopes |8586Once connected, the core signaling/media socket model is shared across products. Meetings, webinars, and Video SDK sessions use the familiar start/stop webhooks. Zoom Contact Center Voice adds its own RTMS/ZCC Voice event family and should be treated as the same transport model with product-specific event payloads.8788## RTMS Overview8990RTMS is a data pipeline that gives your app access to live media from Zoom meetings, webinars, and Video SDK sessions **without participant bots**. Instead of having automated clients join meetings, use RTMS to collect media data directly from Zoom's infrastructure.9192### What RTMS Provides9394| Media Type | Format | Use Cases |95|------------|--------|-----------|96| **Audio** | PCM (L16), G.711, G.722, Opus | Transcription, voice analysis, recording |97| **Video** | H.264, JPG, PNG | Recording, AI vision, thumbnails, active participant selection |98| **Screen Share** | H.264, JPG, PNG | Content capture, slide extraction |99| **Transcript** | JSON text | Meeting notes, search, compliance |100| **Chat** | JSON text | Archive, sentiment analysis |101102### Recent Protocol Changes103104- **Zoom Contact Center Voice support**: RTMS now covers Contact Center Voice audio and transcript scenarios.105- **Transcript Language Identification control**: transcript media handshakes now support `src_language` and `enable_lid`. Default behavior is LID enabled. Set `enable_lid: false` to force a fixed language.106- **Single individual video stream subscription**: RTMS can now stream one participant's camera feed at a time when `data_opt` is set to `VIDEO_SINGLE_INDIVIDUAL_STREAM`.107- **Graceful client-initiated shutdown**: backends can send `STREAM_CLOSE_REQ` over the signaling socket and wait for `STREAM_CLOSE_RESP`.108- **Media keep-alive tolerance increased**: media socket keep-alive timeout is now **65 seconds**, not 35.109- **Audio buffer control in signaling handshake**: signaling handshakes now support optional `buffer_data`. Default is `true`, which delivers up to 60 seconds of initial audio buffered while signaling/media sockets are established. For live streaming pipelines, set `buffer_data: false` to drop the startup backlog and start from live audio once connected.110- **Media payload length field**: audio, video, and screen share media data events can include `content.length`, the original binary length before base64 encoding.111- **ZCC Voice enum update**: `CONFERENCE_PARTICIPANT_LEFT = 29` was added to `RTMS_ZCC_VOICE_EVENT_TYPE`.112113### Two Approaches114115| Approach | Best For | Complexity |116|----------|----------|------------|117| **SDK** (`@zoom/rtms`) | Most use cases | Low - handles WebSocket complexity |118| **Manual WebSocket** | Custom protocols, other languages | High - full protocol implementation |119120## Prerequisites121122- **Node.js 20.3.0+** (24 LTS recommended) for JavaScript SDK123- **Python 3.10+** for Python SDK124- User-managed General App (meetings/webinars), General or S2S app (Contact Center Voice), or Video SDK App (Video SDK) with RTMS enabled125- Webhook endpoint for RTMS events126- Server to receive WebSocket streams127128> **Need to create/configure the Marketplace app first?** Use129> [Marketplace app management](../rest-api/references/marketplace-apps.md) before RTMS130> implementation. It documents General App manifests, event subscription API behavior,131> app-owned `client_credentials`, and RTMS event validation drift. Use the user-managed132> [Meeting/Webinar RTMS template](../rest-api/assets/marketplace-apps/marketplace-manifest-template-for-general-user-meeting-webinar-rtms.json),133> or the General App or S2S Contact Center Voice templates in the134> [template selector](../rest-api/references/marketplace-app-templates.md).135136> **Need RTMS access?** Post in [Zoom Developer Forum](https://devforum.zoom.us/) requesting RTMS access with your use case.137138## Quick Start (SDK - Recommended)139140```javascript141import rtms from "@zoom/rtms";142143// All RTMS start/stop events across products144const RTMS_EVENTS = ["meeting.rtms_started", "webinar.rtms_started", "session.rtms_started"];145146// Handle webhook events147rtms.onWebhookEvent(({ event, payload }) => {148 if (!RTMS_EVENTS.includes(event)) return;149150 const client = new rtms.Client();151152 client.onAudioData((data, timestamp, metadata) => {153 console.log(`Audio from ${metadata.userName}: ${data.length} bytes`);154 });155156 client.onTranscriptData((data, timestamp, metadata) => {157 const text = data.toString('utf8');158 console.log(`${metadata.userName}: ${text}`);159 });160161 client.onJoinConfirm((reason) => {162 console.log(`Joined session: ${reason}`);163 });164165 // SDK handles all WebSocket connections automatically166 // Accepts both meeting_uuid and session_id transparently167 client.join(payload);168});169```170171## Quick Start (Manual WebSocket)172173For full control or non-SDK languages, implement the two-phase WebSocket protocol:174175```javascript176const WebSocket = require('ws');177const crypto = require('crypto');178179const RTMS_EVENTS = ['meeting.rtms_started', 'webinar.rtms_started', 'session.rtms_started'];180181// 1. Generate signature182// For meetings/webinars: uses meeting_uuid. For Video SDK: uses session_id.183function generateSignature(clientId, idValue, streamId, clientSecret) {184 const message = `${clientId},${idValue},${streamId}`;185 return crypto.createHmac('sha256', clientSecret).update(message).digest('hex');186}187188// 2. Handle webhook189app.post('/webhook', (req, res) => {190 res.status(200).send(); // CRITICAL: Respond immediately!191 192 const { event, payload } = req.body;193 if (RTMS_EVENTS.includes(event)) {194 connectToRTMS(payload);195 }196});197198// 3. Connect to signaling WebSocket199function connectToRTMS(payload) {200 const { server_urls, rtms_stream_id } = payload;201 // meeting_uuid for meetings/webinars, session_id for Video SDK202 const idValue = payload.meeting_uuid || payload.session_id;203 const signature = generateSignature(CLIENT_ID, idValue, rtms_stream_id, CLIENT_SECRET);204 205 const signalingWs = new WebSocket(server_urls);206 207 signalingWs.on('open', () => {208 signalingWs.send(JSON.stringify({209 msg_type: 1, // Handshake request210 protocol_version: 1,211 meeting_uuid: idValue,212 rtms_stream_id,213 signature,214 buffer_data: false, // Use false for live streaming; true preserves startup audio215 media_type: 9 // AUDIO(1) | TRANSCRIPT(8)216 }));217 });218 219 // ... handle responses, connect to media WebSocket220}221```222223**See**: [Manual WebSocket Guide](examples/manual-websocket.md) for complete implementation.224225## Media Type Bitmask226227Combine types with bitwise OR:228229| Type | Value | Description |230|------|-------|-------------|231| Audio | 1 | PCM audio samples |232| Video | 2 | H.264/JPG video frames |233| Screen Share | 4 | **Separate from video!** |234| Transcript | 8 | Real-time speech-to-text |235| Chat | 16 | In-meeting chat messages |236| All | 32 | All media types |237238**Example**: Audio + Transcript = `1 | 8` = `9`239240## Critical Gotchas241242| Issue | Solution |243|-------|----------|244| **Only 1 connection allowed** | New connections kick out existing ones. Track active sessions! |245| **Respond 200 immediately** | If webhook delays, Zoom retries creating duplicate connections |246| **Heartbeat mandatory** | Respond to msg_type 12 with msg_type 13, or connection dies |247| **Reconnection is YOUR job** | RTMS doesn't auto-reconnect. Media keep-alive tolerance is now about **65s**; signaling remains around **60s** |248| **Transcript language drift** | Use `src_language` plus `enable_lid: false` when you want fixed-language transcription instead of automatic language switching |249| **Audio params must match the support matrix** | Do not mix arbitrary `content_type`, `codec`, `sample_rate`, `channel`, and `send_rate`. The media server rejects unsupported combinations |250| **Single participant video only** | `VIDEO_SINGLE_INDIVIDUAL_STREAM` supports one participant at a time. A new `VIDEO_SUBSCRIPTION_REQ` overrides the previous selection |251| **Graceful close is explicit now** | Use `STREAM_CLOSE_REQ` / `STREAM_CLOSE_RESP` when your backend wants to terminate the stream cleanly |252| **RTMS manifest validation can drift** | Create-time validation can reject RTMS event names that `PUT /manifest` persists on an existing draft app. Confirm with `GET /manifest` after updates |253254## Environment Variables255256### SDK Environment Variables257258```bash259# Required - Authentication260ZM_RTMS_CLIENT=your_client_id # Zoom OAuth Client ID261ZM_RTMS_SECRET=your_client_secret # Zoom OAuth Client Secret262263# Optional - Webhook server264ZM_RTMS_PORT=8080 # Default: 8080265ZM_RTMS_PATH=/webhook # Default: /266267# Optional - Logging268ZM_RTMS_LOG_LEVEL=info # error, warn, info, debug, trace269ZM_RTMS_LOG_FORMAT=progressive # progressive or json270ZM_RTMS_LOG_ENABLED=true271```272273### Manual Implementation Variables274275```bash276ZOOM_CLIENT_ID=your_client_id277ZOOM_CLIENT_SECRET=your_client_secret278ZOOM_SECRET_TOKEN=your_webhook_token # For webhook validation279```280281## Zoom App Setup282283### For Meetings and Webinars (General App)2842851. Go to [marketplace.zoom.us](https://marketplace.zoom.us) -> Develop -> Build App2862. Choose **General App** -> **User-Managed**2873. Features -> Access -> **Enable Event Subscription**2884. Add Events -> Search "rtms" -> Select:289 - `meeting.rtms_started`290 - `meeting.rtms_stopped`291 - `webinar.rtms_started` (if using webinars)292 - `webinar.rtms_stopped` (if using webinars)2935. Scopes -> Add Scopes -> Search "rtms" -> Add:294 - `meeting:read:meeting_audio`295 - `meeting:read:meeting_video`296 - `meeting:read:meeting_transcript`297 - `meeting:read:meeting_chat`298 - `webinar:read:webinar_audio` (if using webinars)299 - `webinar:read:webinar_video` (if using webinars)300 - `webinar:read:webinar_transcript` (if using webinars)301 - `webinar:read:webinar_chat` (if using webinars)302303### For Video SDK (Video SDK App)3043051. Go to [marketplace.zoom.us](https://marketplace.zoom.us) -> Develop -> Build App3062. Choose **Video SDK App**3073. Use your SDK Key and SDK Secret (not OAuth Client ID/Secret)3084. Add Events:309 - `session.rtms_started`310 - `session.rtms_stopped`311312## Sample Repositories313314### Official Samples315316| Repository | Description |317|------------|-------------|318| [rtms-samples](https://github.com/zoom/rtms-samples) | RTMSManager, boilerplates, AI samples |319| [rtms-quickstart-js](https://github.com/zoom/rtms-quickstart-js) | JavaScript SDK quickstart |320| [rtms-quickstart-py](https://github.com/zoom/rtms-quickstart-py) | Python SDK quickstart |321| [rtms-sdk-cpp](https://github.com/zoom/rtms-sdk-cpp) | C++ SDK |322| [zoom-rtms](https://github.com/zoom/rtms) | Main SDK repository |323324### AI Integration Samples325326| Sample | Description |327|--------|-------------|328| [rtms-meeting-assistant-starter-kit](https://github.com/zoom/rtms-meeting-assistant-starter-kit) | AI meeting assistant with summaries |329| [arlo-meeting-assistant](https://github.com/zoom/arlo-meeting-assistant) | Production meeting assistant with DB |330| [videosdk-rtms-transcribe-audio](https://github.com/zoom/videosdk-rtms-transcribe-audio) | Whisper transcription |331332## Complete Documentation333334### Concepts335- **[Connection Architecture](concepts/connection-architecture.md)** - Two-phase WebSocket design336- **[Lifecycle Flow](concepts/lifecycle-flow.md)** - Webhook to streaming flow337338### Examples339- **[SDK Quickstart](examples/sdk-quickstart.md)** - Using @zoom/rtms SDK340- **[Manual WebSocket](examples/manual-websocket.md)** - Raw protocol implementation341- **[RTMS Bot](examples/rtms-bot.md)** - Complete bot implementation guide342- **[AI Integration](examples/ai-integration.md)** - Transcription and analysis patterns343344### References345- **[Media Types](references/media-types.md)** - Audio, video, transcript, chat, screen share346- **[Data Types](references/data-types.md)** - All enums and constants347- **[Connection](references/connection.md)** - WebSocket protocol details348- **[Webhooks](references/webhooks.md)** - Event subscription349350### Troubleshooting351- **[Common Issues](troubleshooting/common-issues.md)** - FAQ and solutions352353## Resources354355- **Official docs**: https://developers.zoom.us/docs/rtms/356- **Data types**: https://developers.zoom.us/docs/rtms/data-types/357- **Media params**: https://developers.zoom.us/docs/rtms/media-parameter-definition/358- **Developer forum**: https://devforum.zoom.us/359360---361362**Need help?** Start with Integrated Index section below for complete navigation.363364---365366## Integrated Index367368_This section was migrated from `SKILL.md`._369370RTMS provides real-time access to live audio, video, transcript, chat, and screen share from Zoom meetings, webinars, and Video SDK sessions.371372## Critical Positioning373374Treat RTMS as a **backend service** for receiving and processing media streams.375376- Backend role: ingest audio/video/share/chat/transcript, run AI/analytics, persist/forward data.377- Optional frontend role: Zoom App SDK or web dashboard that consumes processed stream data from backend transport (WebSocket/SSE/other).378- Kickoff model: backend waits for RTMS start webhook events, then starts stream processing.379380Do not model RTMS as a frontend-only SDK.381382## Quick Start Path383384**If you're new to RTMS, follow this order:**3853861. **Run preflight checks first** -> [RUNBOOK.md](RUNBOOK.md)3872. **Understand the architecture** -> [concepts/connection-architecture.md](concepts/connection-architecture.md)388 - Two-phase WebSocket: Signaling + Media389 - Why RTMS doesn't use bots3903913. **Choose your approach** -> SDK or Manual392 - SDK (recommended): [examples/sdk-quickstart.md](examples/sdk-quickstart.md)393 - Manual WebSocket: [examples/manual-websocket.md](examples/manual-websocket.md)3943954. **Understand the lifecycle** -> [concepts/lifecycle-flow.md](concepts/lifecycle-flow.md)396 - Webhook -> Signaling -> Media -> Streaming3973985. **Configure media types** -> [references/media-types.md](references/media-types.md)399 - Audio, video, transcript, chat, screen share4004016. **Troubleshoot issues** -> [troubleshooting/common-issues.md](troubleshooting/common-issues.md)402 - Connection problems, duplicate webhooks, missing data403404---405406## Documentation Structure407408```409rtms/410├── SKILL.md # Main skill overview411├── SKILL.md # This file - navigation guide412│413├── concepts/ # Core architectural patterns414│ ├── connection-architecture.md # Two-phase WebSocket design415│ └── lifecycle-flow.md # Webhook to streaming flow416│417├── examples/ # Complete working code418│ ├── sdk-quickstart.md # Using @zoom/rtms SDK419│ ├── manual-websocket.md # Raw protocol implementation420│ ├── rtms-bot.md # Complete RTMS bot implementation421│ └── ai-integration.md # Transcription and analysis422│423├── references/ # Reference documentation424│ ├── media-types.md # Audio, video, transcript, chat, share425│ ├── data-types.md # All enums and constants426│ ├── connection.md # WebSocket protocol details427│ └── webhooks.md # Event subscription428│429└── troubleshooting/ # Problem solving guides430 └── common-issues.md # FAQ and solutions431```432433---434435## By Use Case436437### I want to get meeting transcripts4381. [SDK Quickstart](examples/sdk-quickstart.md) - Fastest approach4392. [Media Types](references/media-types.md#transcript) - Transcript configuration4403. [AI Integration](examples/ai-integration.md) - Whisper, Deepgram, AssemblyAI441442### I want to record meetings4431. [Media Types](references/media-types.md) - Audio + Video configuration4442. [SDK Quickstart](examples/sdk-quickstart.md) - Receiving media4453. [AI Integration](examples/ai-integration.md#audio-recording) - Gap-filled recording446447### I want to build an AI meeting assistant4481. [AI Integration](examples/ai-integration.md) - Complete patterns4492. [SDK Quickstart](examples/sdk-quickstart.md) - Media ingestion4503. [Lifecycle Flow](concepts/lifecycle-flow.md) - Event handling451452### I want to build a complete RTMS bot4531. [RTMS Bot](examples/rtms-bot.md) - **Complete implementation guide**4542. [Lifecycle Flow](concepts/lifecycle-flow.md) - Webhook to streaming flow4553. [Connection Architecture](concepts/connection-architecture.md) - Two-phase design456457### I need full protocol control4581. [Manual WebSocket](examples/manual-websocket.md) - **START HERE**4592. [Connection Architecture](concepts/connection-architecture.md) - Two-phase design4603. [Data Types](references/data-types.md) - All message types and enums4614. [Connection](references/connection.md) - Protocol details462463### I'm getting connection errors4641. [Common Issues](troubleshooting/common-issues.md) - Diagnostic checklist4652. [Connection Architecture](concepts/connection-architecture.md) - Verify flow4663. [Webhooks](references/webhooks.md) - Validation and timing467468### I want to understand the architecture4691. [Connection Architecture](concepts/connection-architecture.md) - Two-phase WebSocket4702. [Lifecycle Flow](concepts/lifecycle-flow.md) - Complete flow diagram4713. [Data Types](references/data-types.md) - Protocol constants472473---474475## By Product476477### I'm building for Zoom Meetings478- Standard RTMS setup. Webhook event: `meeting.rtms_started`. Uses General App with OAuth.479- Start with [SDK Quickstart](examples/sdk-quickstart.md) or [Manual WebSocket](examples/manual-websocket.md).480481### I'm building for Zoom Webinars482- Same as meetings, but webhook event is `webinar.rtms_started`. Payload still uses `meeting_uuid` (NOT `webinar_uuid`).483- Add webinar scopes and event subscriptions. See [Webhooks](references/webhooks.md).484- Only **panelist** streams are confirmed available. Attendee streams may not be individual.485486### I'm building for Zoom Video SDK487- Webhook event: `session.rtms_started`. Payload uses `session_id` (NOT `meeting_uuid`).488- Requires a **Video SDK App** with SDK Key/Secret (not OAuth Client ID/Secret).489- Once connected, the protocol is **identical** to meetings.490- See [Webhooks](references/webhooks.md) for payload details.491492---493494## Key Documents495496### 1. Connection Architecture (CRITICAL)497**[concepts/connection-architecture.md](concepts/connection-architecture.md)**498499RTMS uses **two separate WebSocket connections**:500- **Signaling WebSocket**: Authentication, control, heartbeats501- **Media WebSocket**: Actual audio/video/transcript data502503### 2. SDK vs Manual (DECISION POINT)504**[examples/sdk-quickstart.md](examples/sdk-quickstart.md)** vs **[examples/manual-websocket.md](examples/manual-websocket.md)**505506| SDK | Manual |507|-----|--------|508| Handles WebSocket complexity | Full protocol control |509| Automatic reconnection | DIY reconnection |510| Less code | More code |511| Best for most use cases | Best for custom requirements |512513### 3. Critical Gotchas (MOST COMMON ISSUES)514**[troubleshooting/common-issues.md](troubleshooting/common-issues.md)**5155161. **Respond 200 immediately** - Delayed webhook responses cause duplicates5172. **Only 1 connection per stream** - New connections kick out existing5183. **Heartbeat required** - Must respond to keep-alive or connection dies5194. **Track active sessions** - Prevent duplicate join attempts520521---522523## Key Learnings524525### Critical Discoveries:5265271. **Two-Phase WebSocket Design**528 - Signaling: Control plane (handshake, heartbeat, start/stop)529 - Media: Data plane (audio, video, transcript, chat, share)530 - See: [Connection Architecture](concepts/connection-architecture.md)5315322. **Webhook Response Timing**533 - MUST respond 200 BEFORE any processing534 - Delayed response -> Zoom retries -> duplicate connections535 - See: [Common Issues](troubleshooting/common-issues.md#webhook-response-timing)5365373. **Heartbeat is Mandatory**538 - Signaling: Receive msg_type 12, respond with msg_type 13539 - Media: Same pattern540 - Failure to respond = connection closed541 - See: [Connection](references/connection.md#heartbeat)5425434. **Signature Generation**544 - Format: `HMAC-SHA256(clientSecret, "clientId,meetingUuid,streamId")`545 - For Video SDK, use `session_id` in place of `meetingUuid`546 - Webinars still use `meeting_uuid` (not `webinar_uuid`)547 - Required for both signaling and media handshakes548 - See: [Manual WebSocket](examples/manual-websocket.md#signature-generation)5495505. **Media Types are Bitmasks**551 - Audio=1, Video=2, Share=4, Transcript=8, Chat=16, All=32552 - Combine with OR: Audio+Transcript = 1|8 = 9553 - See: [Media Types](references/media-types.md)5545556. **Startup Audio Buffering is a Signaling Choice**556 - `buffer_data` belongs in `SIGNALING_HAND_SHAKE_REQ`557 - default `true` can replay up to 60s of startup audio558 - set `false` for live streaming or live-only startup behavior559 - See: [Connection](references/connection.md#signaling-handshake-request)5605617. **Screen Share is SEPARATE from Video**562 - Different msg_type (16 vs 15)563 - Different media flag (4 vs 2)564 - Must subscribe separately565 - See: [Media Types](references/media-types.md#screen-share)566567---568569## Quick Reference570571### "Connection fails"572-> [Common Issues](troubleshooting/common-issues.md)573574### "Duplicate connections"575-> [Webhook timing](troubleshooting/common-issues.md#webhook-response-timing)576577### "No audio/video data"578-> [Media Types](references/media-types.md) - Check configuration579580### "How do I implement manually?"581-> [Manual WebSocket](examples/manual-websocket.md)582583### "What message types exist?"584-> [Data Types](references/data-types.md)585586### "How do I integrate AI?"587-> [AI Integration](examples/ai-integration.md)588589---590591## Document Version592593Based on **Zoom RTMS SDK v1.x** and official documentation as of 2026.594595---596597**Happy coding!**598599Remember: Start with [SDK Quickstart](examples/sdk-quickstart.md) for the fastest path, or [Manual WebSocket](examples/manual-websocket.md) if you need full control.