setup-zoom-websockets
anthropics/knowledge-work-plugins
Set up persistent Zoom WebSocket connections for low-latency event delivery as an alternative to webhooks.
What is setup-zoom-websockets?
Reference skill for configuring Zoom WebSockets to receive real-time meeting and user events via persistent bidirectional connections. Use this when you need lower latency, don't want to expose a public endpoint, or have security constraints that make WebSockets preferable to webhooks.
- Establish persistent WebSocket connections to Zoom's event stream using Server-to-Server OAuth
- Receive real-time meeting events (created, started, ended, participant joined/left) and user events
- Handle connection lifecycle including keep-alive heartbeats and automatic reconnection
- Parse and route incoming WebSocket events in JSON format matching webhook event structure
- Compare WebSockets vs webhooks to determine the right integration method for your use case
How to install setup-zoom-websockets
npx skills add https://github.com/anthropics/knowledge-work-plugins --skill setup-zoom-websockets- Server-to-Server OAuth app created in Zoom Marketplace with Account ID, Client ID, and Client Secret
- WebSocket subscription enabled in your Zoom app with specific events selected
- Node.js environment with ws (WebSocket) and axios libraries for the provided code sample
How to use setup-zoom-websockets
- 1.Create a Server-to-Server OAuth app in Zoom Marketplace and copy your Account ID, Client ID, and Client Secret
- 2.Enable WebSocket Event Subscription in your app's Feature settings and select the events you want to receive
- 3.Use the provided code sample to authenticate via OAuth and obtain an access token
- 4.Connect to the WebSocket endpoint using your subscription ID and access token
- 5.Implement event handlers for the event types you subscribed to (meeting.started, participant_joined, etc.)
- 6.Add keep-alive ping logic every 30 seconds to maintain the connection
- 7.Implement reconnection logic to handle unexpected disconnections
Use cases
- Real-time meeting status dashboard that needs sub-second event delivery without polling
- Banking or healthcare application requiring direct secure connections without exposing public webhook endpoints
- AI agent that reacts immediately to meeting state changes (participant joins, recording completion)
- Low-latency participant tracking system for large-scale meeting monitoring
- Application requiring bidirectional communication with Zoom event infrastructure
- Backend engineers building Zoom integrations with strict latency requirements
- Security-conscious teams in regulated industries (finance, healthcare) avoiding public endpoints
- Developers maintaining existing Zoom webhook infrastructure who need to migrate to WebSockets
- AI/ML engineers building real-time meeting analytics or automation
setup-zoom-websockets FAQ
Use WebSockets when you need real-time low-latency updates, want to avoid exposing a public endpoint for security reasons, or need bidirectional communication. Use webhooks when setup simplicity is preferred and you have a small number of event notifications.
No. Only one WebSocket connection can be open per subscription at a time. Opening a new connection will automatically close the existing one.
WebSockets deliver event notifications (meeting created, participant joined, etc.), while RTMS (Realtime Media Streams) provides actual media streams (audio, video, transcripts). Use the rtms skill for media data.
Implement automatic reconnection logic that attempts to reconnect after a delay (e.g., 5 seconds). Also send periodic ping heartbeats every 30 seconds to keep the connection alive and detect stale connections.
Server-to-Server OAuth with account credentials grant type. You exchange your Client ID and Client Secret for an access token, then use that token to establish the WebSocket connection.
Full instructions (SKILL.md)
Source of truth, from anthropics/knowledge-work-plugins.
name: 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 skill for complete authentication flows.
Start troubleshooting fast: Use the 5-Minute Runbook 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 token
async 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 WebSocket
async function connectWebSocket() {
const accessToken = await getAccessToken();
// WebSocket URL from your subscription settings
const 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 types
switch (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 seconds
setInterval(() => {
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 - Connection lifecycle, authentication, error handling
- references/events.md - Complete event types reference
Troubleshooting
- 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.
Related skills
More from anthropics/knowledge-work-plugins and the wider catalog.

signature-request
Prepare and route documents for e-signature with pre-flight checks and signing order configuration.

single-cell-rna-qc
Automated QC for single-cell RNA-seq data with MAD-based filtering and scverse best practices.

slack-messaging
Guidance for composing well-formatted, effective Slack messages using mrkdwn syntax

slack-search
Find messages, files, channels, and people in Slack with effective search strategies and modifiers.

smb-onboard
Onboard SMB owners in 15–20 minutes: connect tools, run a proof-of-value recipe, capture business context, and set a weekly rhythm.

smb-router
Routes small business owners to the right skill based on their immediate need.