Loading...
Loading...
Compare original and translation side by side
build-zoom-botbuild-zoom-bot| Product | Webhook Event | Payload ID | App Type |
|---|---|---|---|
| Meetings | | | General App |
| Webinars | | | General App |
| Video SDK | | | Video SDK App |
| Zoom Contact Center Voice | Product-specific RTMS/ZCC Voice events | Product-specific stream/session identifiers | Contact Center / approved RTMS integration |
| 产品 | Webhook事件 | 负载ID | 应用类型 |
|---|---|---|---|
| 会议 | | | 通用应用 |
| 网络研讨会 | | | 通用应用 |
| Video SDK | | | Video SDK应用 |
| Zoom联络中心语音 | 产品专属的RTMS/ZCC语音事件 | 产品专属的流/会话标识符 | 联络中心/获批的RTMS集成 |
| 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 |
| 媒体类型 | 格式 | 适用场景 |
|---|---|---|
| 音频 | PCM (L16), G.711, G.722, Opus | 转录、语音分析、录制 |
| 视频 | H.264, JPG, PNG | 录制、AI视觉、缩略图、活跃参会者选择 |
| 屏幕共享 | H.264, JPG, PNG | 内容捕获、幻灯片提取 |
| 转录文本 | JSON文本 | 会议纪要、搜索、合规 |
| 聊天 | JSON文本 | 归档、情感分析 |
src_languageenable_lidenable_lid: falsedata_optVIDEO_SINGLE_INDIVIDUAL_STREAMSTREAM_CLOSE_REQSTREAM_CLOSE_RESPsrc_languageenable_lidenable_lid: falsedata_optVIDEO_SINGLE_INDIVIDUAL_STREAMSTREAM_CLOSE_REQSTREAM_CLOSE_RESP| Approach | Best For | Complexity |
|---|---|---|
SDK ( | Most use cases | Low - handles WebSocket complexity |
| Manual WebSocket | Custom protocols, other languages | High - full protocol implementation |
| 实现方式 | 适用场景 | 复杂度 |
|---|---|---|
SDK ( | 绝大多数场景 | 低 - 封装了WebSocket的复杂逻辑 |
| 手动实现WebSocket | 自定义协议、其他编程语言场景 | 高 - 需要完整实现协议逻辑 |
Need RTMS access? Post in Zoom Developer Forum requesting RTMS access with your use case.
需要申请RTMS权限? 在Zoom开发者论坛发帖说明你的使用场景,申请RTMS访问权限。
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);
});import rtms from "@zoom/rtms";
// 覆盖所有产品的RTMS启动/停止事件
const RTMS_EVENTS = ["meeting.rtms_started", "webinar.rtms_started", "session.rtms_started"];
// 处理Webhook事件
rtms.onWebhookEvent(({ event, payload }) => {
if (!RTMS_EVENTS.includes(event)) return;
const client = new rtms.Client();
client.onAudioData((data, timestamp, metadata) => {
console.log(`来自${metadata.userName}的音频:${data.length}字节`);
});
client.onTranscriptData((data, timestamp, metadata) => {
const text = data.toString('utf8');
console.log(`${metadata.userName}: ${text}`);
});
client.onJoinConfirm((reason) => {
console.log(`已加入会话:${reason}`);
});
// SDK自动处理所有WebSocket连接
// 透明兼容meeting_uuid和session_id
client.join(payload);
});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,
media_type: 9 // AUDIO(1) | TRANSCRIPT(8)
}));
});
// ... handle responses, connect to media WebSocket
}const WebSocket = require('ws');
const crypto = require('crypto');
const RTMS_EVENTS = ['meeting.rtms_started', 'webinar.rtms_started', 'session.rtms_started'];
// 1. 生成签名
// 会议/网络研讨会使用meeting_uuid,Video SDK使用session_id
function generateSignature(clientId, idValue, streamId, clientSecret) {
const message = `${clientId},${idValue},${streamId}`;
return crypto.createHmac('sha256', clientSecret).update(message).digest('hex');
}
// 2. 处理Webhook
app.post('/webhook', (req, res) => {
res.status(200).send(); // 关键:必须立即响应!
const { event, payload } = req.body;
if (RTMS_EVENTS.includes(event)) {
connectToRTMS(payload);
}
});
// 3. 连接信令WebSocket
function connectToRTMS(payload) {
const { server_urls, rtms_stream_id } = payload;
// 会议/网络研讨会为meeting_uuid,Video SDK为session_id
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, // 握手请求
protocol_version: 1,
meeting_uuid: idValue,
rtms_stream_id,
signature,
media_type: 9 // 音频(1) | 转录(8)
}));
});
// ... 处理响应、连接媒体WebSocket
}| 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 |
1 | 89| 类型 | 数值 | 说明 |
|---|---|---|
| 音频 | 1 | PCM音频采样 |
| 视频 | 2 | H.264/JPG视频帧 |
| 屏幕共享 | 4 | 和视频是独立类型! |
| 转录 | 8 | 实时语音转文本 |
| 聊天 | 16 | 会议内聊天消息 |
| 全部 | 32 | 所有媒体类型 |
1 | 89| 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 |
| Single participant video only | |
| Graceful close is explicit now | Use |
| 问题 | 解决方案 |
|---|---|
| 仅允许1个连接 | 新连接会踢掉已有连接,需跟踪活跃会话! |
| 立即响应200状态码 | Webhook响应延迟会导致Zoom重试,产生重复连接 |
| 心跳机制是强制要求 | 收到msg_type 12必须回复msg_type 13,否则连接会断开 |
| 重连需要自行实现 | RTMS不会自动重连,媒体保活容错时长约为65秒,信令保活约为60秒 |
| 转录语言漂移 | 如需固定语言转录而非自动切换语言,可设置 |
| 仅支持单参会者视频流 | |
| 优雅关闭需要显式调用 | 后端需要干净终止流时,使用 |
undefinedundefinedundefinedundefinedZOOM_CLIENT_ID=your_client_id
ZOOM_CLIENT_SECRET=your_client_secret
ZOOM_SECRET_TOKEN=your_webhook_token # For webhook validationZOOM_CLIENT_ID=your_client_id
ZOOM_CLIENT_SECRET=your_client_secret
ZOOM_SECRET_TOKEN=your_webhook_token # 用于Webhook校验meeting.rtms_startedmeeting.rtms_stoppedwebinar.rtms_startedwebinar.rtms_stoppedmeeting:read:meeting_audiomeeting:read:meeting_videomeeting:read:meeting_transcriptmeeting:read:meeting_chatwebinar:read:webinar_audiowebinar:read:webinar_videowebinar:read:webinar_transcriptwebinar:read:webinar_chatmeeting.rtms_startedmeeting.rtms_stoppedwebinar.rtms_startedwebinar.rtms_stoppedmeeting:read:meeting_audiomeeting:read:meeting_videomeeting:read:meeting_transcriptmeeting:read:meeting_chatwebinar:read:webinar_audiowebinar:read:webinar_videowebinar:read:webinar_transcriptwebinar:read:webinar_chatsession.rtms_startedsession.rtms_stoppedsession.rtms_startedsession.rtms_stopped| Repository | Description |
|---|---|
| rtms-samples | RTMSManager, boilerplates, AI samples |
| rtms-quickstart-js | JavaScript SDK quickstart |
| rtms-quickstart-py | Python SDK quickstart |
| rtms-sdk-cpp | C++ SDK |
| zoom-rtms | Main SDK repository |
| 仓库 | 说明 |
|---|---|
| rtms-samples | RTMSManager、模板代码、AI示例 |
| rtms-quickstart-js | JavaScript SDK快速入门 |
| rtms-quickstart-py | Python SDK快速入门 |
| rtms-sdk-cpp | C++ SDK |
| zoom-rtms | 核心SDK仓库 |
| Sample | Description |
|---|---|
| rtms-meeting-assistant-starter-kit | AI meeting assistant with summaries |
| arlo-meeting-assistant | Production meeting assistant with DB |
| videosdk-rtms-transcribe-audio | Whisper transcription |
| 示例 | 说明 |
|---|---|
| rtms-meeting-assistant-starter-kit | 支持摘要功能的AI会议助手 |
| arlo-meeting-assistant | 带数据库的生产级会议助手 |
| videosdk-rtms-transcribe-audio | Whisper转录实现 |
SKILL.mdSKILL.mdrtms/
├── 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 solutionsrtms/
├── SKILL.md # 技能总览
├── SKILL.md # 本文件 - 导航指南
│
├── concepts/ # 核心架构模式
│ ├── connection-architecture.md # 两阶段WebSocket设计
│ └── lifecycle-flow.md # Webhook到推流流程
│
├── examples/ # 完整可运行代码
│ ├── sdk-quickstart.md # 使用@zoom/rtms SDK
│ ├── manual-websocket.md # 原生协议实现
│ ├── rtms-bot.md # 完整RTMS机器人实现
│ └── ai-integration.md # 转录和分析
│
├── references/ # 参考文档
│ ├── media-types.md # 音频、视频、转录、聊天、共享
│ ├── data-types.md # 所有枚举和常量
│ ├── connection.md # WebSocket协议详情
│ └── webhooks.md # 事件订阅
│
└── troubleshooting/ # 问题解决指南
└── common-issues.md # 常见问题和解决方案meeting.rtms_startedmeeting.rtms_startedwebinar.rtms_startedmeeting_uuidwebinar_uuidwebinar.rtms_startedmeeting_uuidwebinar_uuidsession.rtms_startedsession_idmeeting_uuidsession.rtms_startedsession_idmeeting_uuid| 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 |
| SDK | 手动实现 |
|---|---|
| 封装了WebSocket的复杂逻辑 | 完整的协议控制权 |
| 自动重连 | 自行实现重连 |
| 代码量更少 | 代码量更多 |
| 适合绝大多数场景 | 适合自定义需求场景 |
HMAC-SHA256(clientSecret, "clientId,meetingUuid,streamId")session_idmeetingUuidmeeting_uuidwebinar_uuidHMAC-SHA256(clientSecret, "clientId,meetingUuid,streamId")session_idmeetingUuidmeeting_uuidwebinar_uuid