iiuniversitet.ruЦентр обучения нейросетямОткрыть каталог

WebSocket Zoom: события в реальном времени

Справочник по постоянному соединению с Zoom: как получать события встреч с малой задержкой без публичного адреса и когда это лучше вебхуков.

СкиллAnthropic (партнёр: Zoom)ClaudeMITНужен терминалПроверка не требуется
Что делает
Справочник по постоянному соединению с Zoom: как получать события встреч с малой задержкой без публичного адреса и когда это лучше вебхуков.
Когда брать
Когда нужны быстрые события Zoom, закрытая от интернета инфраструктура или двусторонняя связь, а вебхуки не подходят.
Когда не брать
Если достаточно простой настройки и редких уведомлений (вебхуки проще) либо нужны аудио, видео и расшифровки встречи (для этого скилл rtms).
Пример запроса
Подключи нас к Zoom по WebSocket, чтобы мы мгновенно узнавали о начале и конце встреч, не открывая публичный адрес.
Нужно подключить
терминал

Входит в плагин zoom-plugin. В Cowork и Claude Code можно поставить плагин целиком.

Как включить

  1. Скачайте архив и распакуйте его.
  2. Положите папку setup-zoom-websockets в ~/.claude/skills/.
  3. Откройте Claude Code и опишите задачу своими словами: Claude подхватит скилл по описанию.

Текст

---
name: setup-zoom-websockets
description: Справочный скилл по WebSocket Zoom. Используй после маршрутизации в рабочий процесс с низкой задержкой, когда постоянные соединения, более быстрая доставка событий или требования безопасности делают WebSocket предпочтительнее вебхуков.
triggers:
  - "zoom websockets"
  - "websocket event subscription"
  - "persistent zoom events"
  - "low latency zoom events"
  - "zoom websocket connection"
---

/setup-zoom-websockets

Справочный материал о постоянных потоках событий Zoom. Сначала определи рабочий процесс через маршрутизацию, а этот файл используй, когда WebSocket, скорее всего, лучше вебхуков.

WebSocket и вебхуки

ПараметрWebSocketВебхуки
СоединениеПостоянное, двустороннееОдноразовый HTTP POST
ЗадержкаНиже (нет накладных расходов HTTP)Выше (новое соединение на каждое событие)
БезопасностьПрямое соединение, открытый адрес не нуженНужна проверка адреса и белый список IP
МодельPull (ты подключаешься к Zoom)Push (Zoom подключается к тебе)
СостояниеС сохранением состояния (соединение поддерживается)Без состояния (каждое событие независимо)
НастройкаСложнее (токен доступа, соединение)Проще (достаточно адреса)

Выбирай WebSocket, когда:

  • Важны обновления в реальном времени с малой задержкой
  • Безопасность превыше всего (банки, здравоохранение, финансы)
  • Ты не хочешь открывать публичный адрес
  • Нужна двусторонняя связь

Выбирай вебхуки, когда:

  • Предпочтительна более простая настройка
  • Событий для уведомления немного
  • Уже есть HTTP-инфраструктура

Предварительные требования

  • Приложение Server-to-Server OAuth в Zoom Marketplace
  • Account ID, Client ID и Client Secret
  • Подписка WebSocket с включёнными событиями

Нужна помощь с S2S OAuth? Полные схемы аутентификации смотри в скилле [zoom-oauth](../oauth/SKILL.md).

Быстрее найти причину неполадки: воспользуйся [пятиминутным RUNBOOK](RUNBOOK.md) до глубокой отладки.

Быстрый старт

1. Создай приложение Server-to-Server OAuth

  1. Открой Zoom Marketplace
  2. Создай приложение Server-to-Server OAuth
  3. Скопируй Account ID, Client ID, Client Secret

2. Включи подписку WebSocket

  1. В приложении перейди в Feature → Event Subscriptions
  2. Добавь подписку на события (Event Subscription)
  3. В качестве типа метода выбери WebSockets
  4. Выбери события, на которые подписываешься (например, meeting.created, meeting.started)
  5. Сохрани: будет создан адрес для подключения

3. Подключись по 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();

Формат события

События, полученные по WebSocket, имеют тот же формат, что и события вебхуков:

{
  "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"
    }
  }
}

Частые события

СобытиеОписание
meeting.createdВстреча запланирована
meeting.updatedИзменены настройки встречи
meeting.deletedВстреча удалена
meeting.startedВстреча началась
meeting.endedВстреча закончилась
meeting.participant_joinedУчастник вошёл во встречу
meeting.participant_leftУчастник вышел из встречи
recording.completedОблачная запись готова
user.createdДобавлен новый пользователь
user.updatedИзменены данные пользователя

Управление соединением

Поддержание соединения (keep-alive)

WebSocket-соединениям нужны периодические проверочные сигналы (heartbeat). Zoom закрывает простаивающие соединения.

// Send ping every 30 seconds
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.ping();
  }
}, 30000);

Переподключение

Для надёжности реализуй автоматическое переподключение:

function connectWithReconnect() {
  const ws = connectWebSocket();
  
  ws.on('close', () => {
    console.log('Connection lost. Reconnecting in 5 seconds...');
    setTimeout(connectWithReconnect, 5000);
  });
  
  return ws;
}

Ограничение: одно соединение

Важно: на одну подписку одновременно может быть открыто только ОДНО WebSocket-соединение. Новое соединение закрывает прежнее.

Подробные справочники

  • [references/connection.md](references/connection.md) - Жизненный цикл соединения, аутентификация, обработка ошибок
  • [references/events.md](references/events.md) - Полный справочник типов событий

Устранение неполадок

  • [troubleshooting/common-issues.md](troubleshooting/common-issues.md) - Путаница с адресом подписки, разрывы соединения, отладка при отсутствии событий

Примеры репозиториев

Официальные / от сообщества

ТипРепозиторийОписание
Node.jsjust-zoomit/zoom-websocketsПример WebSocket с S2S OAuth

WebSocket и RTMS

Не путай WebSocket с RTMS (Realtime Media Streams, потоки медиа в реальном времени):

ПараметрWebSocketRTMS
НазначениеУведомления о событияхМедиапотоки
ДанныеСобытия встреч и пользователейАудио, видео, расшифровки
СценарийРеагировать на события ZoomИИ/МО, живая расшифровка
СкиллЭтот скиллrtms

Для аудио, видео и расшифровок в реальном времени используй скилл rtms.

Ресурсы

Переменные окружения

  • Стандартизированные ключи .env и где найти каждое значение — в [references/environment-variables.md](references/environment-variables.md).

Перевод: iiuniversitet. Оригинал: https://github.com/anthropics/knowledge-work-plugins/tree/main/partner-built/zoom-plugin/skills/websockets, лицензия MIT. Изменения: перевод на русский язык.

Оригинал на английском
---
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/knowledge-work-plugins / zoom-plugin / setup-zoom-websockets ↗. Ссылка проверена 2026-10-10.