Skill v1.0.0
currentTrusted Publisher100/100name: setup-zoom-websockets description: Reference skill for Zoom WebSockets. Use after routing to a low-latency event workflow when persistent connections, faster event delivery, or security constraints make WebSockets preferable to webhooks. triggers:
- "zoom websockets"
- "websocket event subscription"
- "persistent zoom events"
- "low latency zoom events"
- "zoom websocket connection"
/setup-zoom-websockets
Background reference for persistent Zoom event streams. Prefer workflow routing first, then use this file when WebSockets are plausibly better than webhooks.
WebSockets vs Webhooks
| Aspect | WebSockets | Webhooks | |
|---|---|---|---|
| Connection | Persistent, bidirectional | One-time HTTP POST | |
| Latency | Lower (no HTTP overhead) | Higher (new connection per event) | |
| Security | Direct connection, no exposed endpoint | Requires endpoint validation, IP whitelisting | |
| Model | Pull (you connect to Zoom) | Push (Zoom connects to you) | |
| State | Stateful (maintains connection) | Stateless (each event independent) | |
| Setup | More complex (access token, connection) | Simpler (just endpoint URL) |
Choose WebSockets when:
- Real-time, low-latency updates are critical
- Security is paramount (banking, healthcare, finance)
- You don't want to expose a public endpoint
- You need bidirectional communication
Choose Webhooks when:
- Simpler setup is preferred
- Small number of event notifications
- Existing HTTP infrastructure
Prerequisites
- Server-to-Server OAuth app in Zoom Marketplace
- Account ID, Client ID, and Client Secret
- WebSocket subscription with events enabled
Need help with S2S OAuth? See the [zoom-oauth](../oauth/SKILL.md) skill for complete authentication flows.
Start troubleshooting fast: Use the [5-Minute Runbook](RUNBOOK.md) before deep debugging.
Quick Start
1. Create Server-to-Server OAuth App
- Go to Zoom Marketplace
- Create a Server-to-Server OAuth app
- Copy Account ID, Client ID, Client Secret
2. Enable WebSocket Subscription
- In your app, go to Feature → Event Subscriptions
- Add an Event Subscription
- Select WebSockets as the method type
- Select events to subscribe to (e.g.,
meeting.created,meeting.started) - Save - an endpoint URL will be generated
3. Connect via WebSocket
const WebSocket = require('ws');const axios = require('axios');// Step 1: Get access tokenasync function getAccessToken() {const credentials = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64');const response = await axios.post('https://zoom.us/oauth/token',new URLSearchParams({grant_type: 'account_credentials',account_id: ACCOUNT_ID}),{headers: {'Authorization': `Basic ${credentials}`,'Content-Type': 'application/x-www-form-urlencoded'}});return response.data.access_token;}// Step 2: Connect to WebSocketasync function connectWebSocket() {const accessToken = await getAccessToken();// WebSocket URL from your subscription settingsconst wsUrl = `wss://ws.zoom.us/ws?subscriptionId=${SUBSCRIPTION_ID}&access_token=${accessToken}`;const ws = new WebSocket(wsUrl);ws.on('open', () => {console.log('WebSocket connection established');});ws.on('message', (data) => {const event = JSON.parse(data);console.log('Event received:', event.event);// Handle different event typesswitch (event.event) {case 'meeting.started':console.log(`Meeting started: ${event.payload.object.topic}`);break;case 'meeting.ended':console.log(`Meeting ended: ${event.payload.object.uuid}`);break;case 'meeting.participant_joined':console.log(`Participant joined: ${event.payload.object.participant.user_name}`);break;}});ws.on('close', (code, reason) => {console.log(`Connection closed: ${code} - ${reason}`);// Implement reconnection logic});ws.on('error', (error) => {console.error('WebSocket error:', error);});return ws;}connectWebSocket();
Event Format
Events received via WebSocket have the same format as webhook events:
{"event": "meeting.started","event_ts": 1706123456789,"payload": {"account_id": "abcD3ojkdbjfg","object": {"id": 1234567890,"uuid": "abcdefgh-1234-5678-abcd-1234567890ab","host_id": "xyz789","topic": "Team Standup","type": 2,"start_time": "2024-01-25T10:00:00Z","timezone": "America/Los_Angeles"}}}
Common Events
| Event | Description | |
|---|---|---|
meeting.created | Meeting scheduled | |
meeting.updated | Meeting settings changed | |
meeting.deleted | Meeting deleted | |
meeting.started | Meeting begins | |
meeting.ended | Meeting ends | |
meeting.participant_joined | Participant joins meeting | |
meeting.participant_left | Participant leaves meeting | |
recording.completed | Cloud recording ready | |
user.created | New user added | |
user.updated | User details changed |
Connection Management
Keep-Alive
WebSocket connections require periodic heartbeats. Zoom will close idle connections.
// Send ping every 30 secondssetInterval(() => {if (ws.readyState === WebSocket.OPEN) {ws.ping();}}, 30000);
Reconnection
Implement automatic reconnection for reliability:
function connectWithReconnect() {const ws = connectWebSocket();ws.on('close', () => {console.log('Connection lost. Reconnecting in 5 seconds...');setTimeout(connectWithReconnect, 5000);});return ws;}
Single Connection Limit
Important: Only ONE WebSocket connection can be open per subscription at a time. Opening a new connection will close the existing one.
Detailed References
- [references/connection.md](references/connection.md) - Connection lifecycle, authentication, error handling
- [references/events.md](references/events.md) - Complete event types reference
Troubleshooting
- [troubleshooting/common-issues.md](troubleshooting/common-issues.md) - Subscription URL confusion, disconnects, no-events debugging
Sample Repositories
Official / Community
| Type | Repository | Description | |
|---|---|---|---|
| Node.js | just-zoomit/zoom-websockets | WebSocket sample with S2S OAuth |
WebSockets vs RTMS
Don't confuse WebSockets with RTMS (Realtime Media Streams):
| Feature | WebSockets | RTMS | |
|---|---|---|---|
| Purpose | Event notifications | Media streams | |
| Data | Meeting events, user events | Audio, video, transcripts | |
| Use case | React to Zoom events | AI/ML, live transcription | |
| Skill | This skill | rtms |
For real-time audio/video/transcript data, use the rtms skill instead.
Resources
- WebSockets docs: https://developers.zoom.us/docs/api/websockets/
- Webhooks comparison: https://www.zoom.com/en/blog/a-guide-to-webhooks-and-websockets/
- Developer forum: https://devforum.zoom.us/
Environment Variables
- See references/environment-variables.md for standardized
.envkeys and where to find each value.