setup-zoom-websockets
$
npx mdskill add anthropics/knowledge-work-plugins/setup-zoom-websocketsEstablish persistent Zoom connections for low-latency event streams.
- Enables real-time updates for critical banking or healthcare workflows.
- Integrates with Zoom's WebSocket API for bidirectional communication.
- Selects connection mode based on latency and security requirements.
- Delivers events directly without exposing public endpoints.
SKILL.md
.github/skills/setup-zoom-websocketsView on GitHub ↗
---
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](https://marketplace.zoom.us/)
- 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
1. Go to [Zoom Marketplace](https://marketplace.zoom.us/develop/create)
2. Create a **Server-to-Server OAuth** app
3. Copy Account ID, Client ID, Client Secret
### 2. Enable WebSocket Subscription
1. In your app, go to **Feature** → **Event Subscriptions**
2. Add an Event Subscription
3. Select **WebSockets** as the method type
4. Select events to subscribe to (e.g., `meeting.created`, `meeting.started`)
5. Save - an endpoint URL will be generated
### 3. Connect via WebSocket
```javascript
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:
```json
{
"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.
```javascript
// Send ping every 30 seconds
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping();
}
}, 30000);
```
### Reconnection
Implement automatic reconnection for reliability:
```javascript
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](https://github.com/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](references/environment-variables.md) for standardized `.env` keys and where to find each value.
More from anthropics/knowledge-work-plugins
- accessibility-reviewRun a WCAG 2.1 AA accessibility audit on a design or page. Trigger with "audit accessibility", "check a11y", "is this accessible?", or when reviewing a design for color contrast, keyboard navigation, touch target size, or screen reader behavior before handoff.
- account-research"Research a company using Common Room data. Triggers on 'research [company]', 'tell me about [domain]', 'pull up signals for [account]', 'what's going on with [company]', or any account-level question."
- analyzeAnswer data questions -- from quick lookups to full analyses. Use when looking up a single metric, investigating what's driving a trend or drop, comparing segments over time, or preparing a formal data report for stakeholders.
- architectureCreate or evaluate an architecture decision record (ADR). Use when choosing between technologies (e.g., Kafka vs SQS), documenting a design decision with trade-offs and consequences, reviewing a system design proposal, or designing a new component from requirements and constraints.
- audit-supportSupport SOX 404 compliance with control testing methodology, sample selection, and documentation standards. Use when generating testing workpapers, selecting audit samples, classifying control deficiencies, or preparing for internal or external audits.
- brand-reviewReview content against your brand voice, style guide, and messaging pillars, flagging deviations by severity with specific before/after fixes. Use when checking a draft before it ships, when auditing copy for voice consistency and terminology, or when screening for unsubstantiated claims, missing disclaimers, and other legal flags.
- brand-voice-enforcement>
- briefGenerate contextual briefings for legal work — daily summary, topic research, or incident response. Use when starting your day and need a scan of legal-relevant items across email, calendar, and contracts, when researching a specific legal question across internal sources, or when a developing situation (data breach, litigation threat, regulatory inquiry) needs rapid context.
- build-dashboardBuild an interactive HTML dashboard with charts, filters, and tables. Use when creating an executive overview with KPI cards, turning query results into a shareable self-contained report, building a team monitoring snapshot, or needing multiple charts with filters in one browser-openable file.
- build-zoom-botBuild a Zoom meeting bot, recorder, or real-time media workflow. Use when joining meetings programmatically, processing live media or transcripts, or combining Meeting SDK, RTMS, and backend services.