276 lines
7.4 KiB
Markdown
276 lines
7.4 KiB
Markdown
# RTMS - Webhooks
|
|
|
|
RTMS-related webhook events and configuration.
|
|
|
|
## CRITICAL: Respond 200 IMMEDIATELY!
|
|
|
|
**The #1 cause of random disconnects:**
|
|
|
|
If your webhook handler takes too long to respond, Zoom assumes failure and retries. The retry creates a second connection, which kicks out your first connection (only 1 connection allowed per stream).
|
|
|
|
```javascript
|
|
// CORRECT: Respond first, process async
|
|
app.post('/webhook', (req, res) => {
|
|
res.status(200).send(); // IMMEDIATELY!
|
|
|
|
// Then process asynchronously
|
|
handleRTMSEvent(req.body);
|
|
});
|
|
|
|
// WRONG: Processing before responding
|
|
app.post('/webhook', async (req, res) => {
|
|
await heavyProcessing(req.body); // Zoom may retry while waiting!
|
|
res.status(200).send();
|
|
});
|
|
```
|
|
|
|
## URL Validation Challenge
|
|
|
|
When configuring your webhook URL, Zoom sends a validation challenge:
|
|
|
|
```javascript
|
|
app.post('/webhook', (req, res) => {
|
|
const { event, payload } = req.body;
|
|
|
|
// Handle URL validation
|
|
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
|
|
});
|
|
}
|
|
|
|
res.status(200).send();
|
|
// ... handle other events
|
|
});
|
|
```
|
|
|
|
## Events
|
|
|
|
### meeting.rtms_started
|
|
|
|
Sent when RTMS stream is ready for a meeting.
|
|
|
|
```json
|
|
{
|
|
"event": "meeting.rtms_started",
|
|
"payload": {
|
|
"account_id": "account_id",
|
|
"object": {
|
|
"meeting_id": "meeting_id",
|
|
"meeting_uuid": "meeting_uuid",
|
|
"host_id": "host_user_id",
|
|
"rtms_stream_id": "stream_id",
|
|
"server_urls": "wss://rtms-sjc1.zoom.us/...",
|
|
"signature": "auth_signature"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### meeting.rtms_stopped
|
|
|
|
Sent when RTMS stream ends.
|
|
|
|
```json
|
|
{
|
|
"event": "meeting.rtms_stopped",
|
|
"payload": {
|
|
"account_id": "account_id",
|
|
"object": {
|
|
"meeting_id": "meeting_id",
|
|
"rtms_stream_id": "stream_id"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### webinar.rtms_started
|
|
|
|
Sent when RTMS stream is ready for a webinar.
|
|
|
|
```json
|
|
{
|
|
"event": "webinar.rtms_started",
|
|
"payload": {
|
|
"account_id": "account_id",
|
|
"object": {
|
|
"meeting_id": "meeting_id",
|
|
"meeting_uuid": "meeting_uuid",
|
|
"host_id": "host_user_id",
|
|
"rtms_stream_id": "stream_id",
|
|
"server_urls": "wss://rtms-sjc1.zoom.us/...",
|
|
"signature": "auth_signature"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
> **Important**: Webinar payloads use `meeting_uuid`, NOT `webinar_uuid`. The signature and connection flow are identical to meetings.
|
|
|
|
**Webinar-specific considerations:**
|
|
- **Panelists**: Full audio/video streams are available for panelists.
|
|
- **Attendees**: View-only participants; individual streams may not be available.
|
|
- **Practice sessions**: Not documented for RTMS.
|
|
- **Q&A/Polls**: Not exposed via RTMS.
|
|
|
|
### webinar.rtms_stopped
|
|
|
|
Sent when RTMS stream ends for a webinar.
|
|
|
|
```json
|
|
{
|
|
"event": "webinar.rtms_stopped",
|
|
"payload": {
|
|
"account_id": "account_id",
|
|
"object": {
|
|
"meeting_id": "meeting_id",
|
|
"rtms_stream_id": "stream_id"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### session.rtms_started
|
|
|
|
Sent when RTMS stream is ready for a Video SDK session.
|
|
|
|
```json
|
|
{
|
|
"event": "session.rtms_started",
|
|
"payload": {
|
|
"account_id": "account_id",
|
|
"object": {
|
|
"session_id": "session_id",
|
|
"rtms_stream_id": "stream_id",
|
|
"server_urls": "wss://rtms-sjc1.zoom.us/...",
|
|
"signature": "auth_signature"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
> **Important**: Video SDK payloads use `session_id` instead of `meeting_uuid`. The HMAC signature must use `session_id` in place of `meeting_uuid`.
|
|
|
|
**Video SDK-specific considerations:**
|
|
- Uses **SDK Key/Secret** (not OAuth Client ID/Secret) for authentication.
|
|
- Requires a **Video SDK App** (not a General App) in Zoom Marketplace.
|
|
- Once connected, the WebSocket protocol is identical to meetings.
|
|
|
|
### session.rtms_stopped
|
|
|
|
Sent when RTMS stream ends for a Video SDK session.
|
|
|
|
```json
|
|
{
|
|
"event": "session.rtms_stopped",
|
|
"payload": {
|
|
"account_id": "account_id",
|
|
"object": {
|
|
"session_id": "session_id",
|
|
"rtms_stream_id": "stream_id"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Screen Share Events (via msg_type 5)
|
|
|
|
Subscribe to receive `SHARING_START` and `SHARING_STOP` events when participants start/stop screen sharing.
|
|
|
|
## Payload Fields
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| `rtms_stream_id` | Unique stream identifier |
|
|
| `server_urls` | WebSocket signaling server URL |
|
|
| `meeting_uuid` | Meeting unique identifier (needed for signature) |
|
|
| `signature` | Pre-computed auth signature (alternative to self-generating) |
|
|
|
|
## Server URL Geo-Routing
|
|
|
|
Server URLs contain airport/region codes:
|
|
|
|
| Code | Location |
|
|
|------|----------|
|
|
| `sjc` | San Jose, California |
|
|
| `iad` | Washington DC |
|
|
| `sin` | Singapore |
|
|
| `fra` | Frankfurt, Germany |
|
|
| `syd` | Sydney, Australia |
|
|
|
|
```javascript
|
|
// Extract region from server URL
|
|
const hostname = new URL(serverUrl).hostname; // rtms-sjc1.zoom.us
|
|
const region = hostname.split('-')[1].replace(/[0-9]/g, ''); // sjc
|
|
```
|
|
|
|
**Tip**: For production, route webhooks to workers in the same region as the Zoom server.
|
|
|
|
## Subscribing to RTMS Events
|
|
|
|
### In Zoom Marketplace (General App - Meetings and Webinars)
|
|
|
|
1. Go to your app settings
|
|
2. Navigate to **Features** → **Access**
|
|
3. **Enable Event Subscription**
|
|
4. Click **Add Event Subscription**
|
|
5. Enter your webhook endpoint URL
|
|
6. Search "rtms" and select:
|
|
- `meeting.rtms_started`
|
|
- `meeting.rtms_stopped`
|
|
- `webinar.rtms_started` (if using webinars)
|
|
- `webinar.rtms_stopped` (if using webinars)
|
|
7. Click **Done** then **Save**
|
|
|
|
### In Zoom Marketplace (Video SDK App)
|
|
|
|
1. Go to your Video SDK app settings
|
|
2. Add Event Subscription:
|
|
- `session.rtms_started`
|
|
- `session.rtms_stopped`
|
|
|
|
### Required Scopes
|
|
|
|
**For Meetings** (Features → Scopes → Add Scopes → search "rtms"):
|
|
|
|
| Scope | Purpose |
|
|
|-------|---------|
|
|
| `meeting:read:meeting_audio` | Access meeting audio |
|
|
| `meeting:read:meeting_video` | Access meeting video |
|
|
| `meeting:read:meeting_transcript` | Access transcripts |
|
|
| `meeting:read:meeting_chat` | Access chat messages |
|
|
|
|
**For Webinars** (add these in addition to meeting scopes):
|
|
|
|
| Scope | Purpose |
|
|
|-------|---------|
|
|
| `webinar:read:webinar_audio` | Access webinar audio |
|
|
| `webinar:read:webinar_video` | Access webinar video |
|
|
| `webinar:read:webinar_transcript` | Access webinar transcripts |
|
|
| `webinar:read:webinar_chat` | Access webinar chat messages |
|
|
|
|
**For Video SDK**: Uses SDK Key/Secret credentials instead of OAuth scopes.
|
|
|
|
## Products Supporting RTMS
|
|
|
|
| Product | Start Event | Stop Event | Payload ID | App Type |
|
|
|---------|-------------|------------|------------|----------|
|
|
| **Zoom Meetings** | `meeting.rtms_started` | `meeting.rtms_stopped` | `meeting_uuid` | General App |
|
|
| **Zoom Webinars** | `webinar.rtms_started` | `webinar.rtms_stopped` | `meeting_uuid` (not webinar_uuid!) | General App |
|
|
| **Zoom Video SDK** | `session.rtms_started` | `session.rtms_stopped` | `session_id` | Video SDK App |
|
|
| Zoom Contact Center | `contactcenter.rtms_*` | `contactcenter.rtms_*` | See Zoom docs | Contact Center App |
|
|
| Zoom Phone | `phone.rtms_*` | `phone.rtms_*` | See Zoom docs | General App |
|
|
|
|
> **Key differences**: Meetings and webinars use a General App with OAuth credentials. Video SDK uses a Video SDK App with SDK Key/Secret. Once connected, the WebSocket protocol is identical across all products.
|
|
|
|
## Resources
|
|
|
|
- **Event reference**: https://developers.zoom.us/docs/rtms/event-reference/
|
|
- **RTMS docs**: https://developers.zoom.us/docs/rtms/
|