Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot official · Anthropic

setup-zoom-websockets — Claude Code skill: Zoom events

This skill walks through receiving Zoom events over a persistent WebSocket connection. Use it when low latency or avoiding a public endpoint matters.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add anthropics/knowledge-work-plugins --skill setup-zoom-websockets -a claude-code
npx skills add anthropics/knowledge-work-plugins --skill websockets -a claude-code
Open on GitHub

What setup-zoom-websockets does

The skill covers setting up a Zoom event subscription over WebSockets: create a Server-to-Server OAuth app, choose events, and connect with an access token. Its JavaScript examples handle meeting events and reconnecting. WebSocket events use the same format as webhook events.

You need an Account ID, Client ID, Client Secret, and a WebSocket subscription with events enabled. Connections need periodic heartbeats, and only one connection can be open per subscription; opening another closes the current one. That limit matters when multiple app instances share a subscription. WebSockets suit persistent, bidirectional communication, while the guide points to webhooks for simpler setup and a small number of notifications.

What it helps with

  • connect an app to Zoom events over WebSockets
  • receive meeting start and end events
  • handle participants joining meetings
  • reconnect after a dropped connection

Example prompts for the agent

  • Set up a Zoom WebSocket subscription for meeting start and end events.
  • Show me how to receive meeting.participant_joined events over Zoom WebSockets.
  • Add reconnection and heartbeat handling to a Zoom WebSocket connection.

How to install setup-zoom-websockets

1Skills CLI (Vercel)

npx skills add anthropics/knowledge-work-plugins --skill setup-zoom-websockets -a claude-code
npx skills add anthropics/knowledge-work-plugins --skill websockets -a claude-code

2Manually: put the files in place

~/.claude/skills/websockets/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/websockets/

From the same set133

The whole set →
data-visualization official anthropics Selects chart types based on data structure and produces Python visualization code in matplotlib, seaborn, and plotly. Useful for reports and research figures. Claude Code CX Codex Cursor +1 13,700installs documentation official anthropics A skill for technical writing that drafts READMEs, API references, runbooks, architecture docs and onboarding guides. Useful when the code exists but the description does not. Claude Code CX Codex Cursor +1 10,300installs build-dashboard official anthropics Builds a self-contained HTML dashboard with Chart.js charts, KPI cards, and filterable tables. Useful for turning raw data or queries into a standalone browser report. Claude Code CX Codex Cursor +1 9,100installs memory-management official anthropics This skill organizes workplace shorthand, names, and project details into files an agent can consult. It fits projects where requests rely on an internal vocabulary and shared context. Claude Code CX Codex Cursor +1 7,400installs task-management official anthropics Tracks commitments, active work, and follow-ups in a local TASKS.md file. Useful when managing project to-dos directly from the AI coding chat. Claude Code CX Codex Cursor +1 7,200installs architecture official anthropics Writes an Architecture Decision Record or reviews a system design: options, trade-offs, consequences. Useful when you have to pick between technologies. Claude Code CX Codex Cursor +1 6,700installs knowledge-synthesis official anthropics Merges search results across chats, emails, and documents into deduplicated answers with source attribution. Flags conflicting facts and scores source reliability. Claude Code CX Codex Cursor +1 6,400installs testing-strategy official anthropics This skill designs testing strategies and test plans for a project. Use it when you need test types, coverage goals, or gaps in existing checks. Claude Code CX Codex Cursor +1 6,400installs

Similar

prisma-compute prisma Skill for deploying TypeScript apps to Prisma Compute via Prisma 8 CLI. Manage deployments, versions, environment variables, and custom domains from your editor. Claude Code CX Codex Cursor +1 370,000installs prisma-mongodb-upgrade prisma Decision guide for Prisma ORM MongoDB projects on v6 to navigate the lack of Prisma 7 support and evaluate moving to Prisma 8. Claude Code CX Codex Cursor +1 368,500installs neon-postgres neondatabase This skill guides agents through Neon Postgres connections, schema changes, branching, and database checks. Use it when working with an existing schema or DATABASE_URL. Claude Code CX Codex Cursor +1 278,200installs cmux-billing manaflow-ai Runbook for cmux billing architecture covering Stripe Checkout, subscription tiers, webhooks, and entitlement overrides during backend development. Claude Code CX Codex Cursor +1 3,700installs mcp-builder official anthropics This skill walks through building an MCP server in TypeScript or Python, from reading the protocol spec to writing tests and evaluation questions. Claude Code Claude.ai 130,949installs lark-im larksuite This skill lets an agent work with Lark IM messages, chats, files, and interactive cards. Use it when coding tasks need access to team conversations. Claude Code CX Codex Cursor +1 472,300installs lark-event larksuite Listens to Lark/Feishu events and streams them as NDJSON. Use it for bots, message processing, and long-running subscribers. Claude Code CX Codex Cursor +1 466,200installs caveman-discover JuliusBrussee Finds and labels every LLM workflow in your repository so Caveman Cloud breaks down spend by actual job rather than grouping everything into one bucket. Claude Code CX Codex Cursor +1 103,500installs

Questions about setup-zoom-websockets

How do I install setup-zoom-websockets in Claude Code?

Run npx skills add anthropics/knowledge-work-plugins --skill setup-zoom-websockets -a claude-code. The skill installs to ~/.claude/skills/websockets/SKILL.md.

What does setup-zoom-websockets do?

It explains how to create a Zoom WebSocket subscription, obtain an access token, and handle meeting and user events.

What do I need to connect to Zoom WebSockets?

You need a Server-to-Server OAuth app, Account ID, Client ID, Client Secret, and a WebSocket subscription with events enabled. Only one connection can be open per subscription.

Source text

SKILL.md · Apache-2.0
---
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.

© anthropics · Apache-2.0 · anthropics/knowledge-work-plugins

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor