
Setup Zoom Websockets
Connect your solo-built app to Zoom’s real-time WebSocket feeds with Server-to-Server OAuth, token refresh, and reconnect handling.
Overview
Setup Zoom WebSockets is an agent skill for the Build phase that documents Server-to-Server OAuth and WebSocket connection lifecycle management for Zoom real-time events.
Install
npx skills add https://github.com/anthropics/knowledge-work-plugins --skill setup-zoom-websocketsWhat is this skill?
- Documents a 5-step connection lifecycle from S2S OAuth through reconnect and clean shutdown
- JavaScript example for account_credentials grant and Basic-auth token request to zoom.us/oauth/token
- Token refresh guidance for the usual 3600-second access token expiry
- ZoomWebSocketClient-style pattern for holding accountId, clientId, clientSecret, and subscriptionId
- Focused on WebSocket authentication and connection management—not full Zoom app marketplace setup
- 5-step WebSocket connection lifecycle diagram
- Access tokens typically expire in 3600 seconds (1 hour)
Adoption & trust: 839 installs on skills.sh; 19.6k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You have Zoom S2S credentials but no clear, refresh-safe pattern for opening WebSockets and keeping them alive through token expiry and disconnects.
Who is it for?
Solo builders adding Zoom live-event streams to a Node or JavaScript backend after scopes and subscription IDs are already chosen.
Skip if: Teams that only need REST polling, lack Zoom S2S OAuth setup, or want a full Zoom app onboarding checklist without WebSockets.
When should I use this skill?
Implementing or debugging Zoom WebSocket connections that require S2S OAuth tokens and reconnect handling.
What do I get? / Deliverables
You get a documented lifecycle and starter client pattern so your agent can implement authenticated Zoom WebSocket connections with scheduled token refresh and reconnect handling.
- OAuth token helper aligned to account_credentials grant
- WebSocket client skeleton with refresh and reconnect hooks
- Documented open → event → reconnect → close flow for your repo
Recommended Skills
Journey fit
The skill is a connection-management reference for wiring Zoom WebSockets into a product—canonical shelf is Build integrations, not launch or ops runbooks. Content covers OAuth token exchange, WebSocket open/close, and client-class lifecycle patterns typical of third-party API integration work.
How it compares
Use as a focused WebSocket connection cookbook rather than ad-hoc copy-paste from scattered Zoom forum snippets.
Common Questions / FAQ
Who is setup-zoom-websockets for?
Indie and solo builders wiring Zoom real-time events into saas or agent backends who already use Server-to-Server OAuth and need WebSocket connection discipline.
When should I use setup-zoom-websockets?
During Build → integrations when you implement Zoom WebSocket subscribers, refresh tokens before the hourly expiry, or design reconnect logic for production listeners.
Is setup-zoom-websockets safe to install?
Treat it as documentation that guides code touching client secrets and live APIs; review the Security Audits panel on this Prism page and rotate credentials if examples are pasted into repos.
SKILL.md
READMESKILL.md - Setup Zoom Websockets
# WebSockets - Connection Management Detailed guide for managing WebSocket connections to Zoom. ## Connection Lifecycle ``` 1. Generate access token (S2S OAuth) ↓ 2. Open WebSocket connection with token ↓ 3. Receive events in real-time ↓ 4. Handle disconnects and reconnect ↓ 5. Close connection when done ``` ## Authentication WebSocket connections require a valid Server-to-Server OAuth access token. ### Generate Access Token ```javascript const axios = require('axios'); async function getAccessToken(accountId, clientId, clientSecret) { const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); const response = await axios.post( 'https://zoom.us/oauth/token', new URLSearchParams({ grant_type: 'account_credentials', account_id: accountId }), { headers: { 'Authorization': `Basic ${credentials}`, 'Content-Type': 'application/x-www-form-urlencoded' } } ); return { accessToken: response.data.access_token, expiresIn: response.data.expires_in // Usually 3600 seconds (1 hour) }; } ``` ### Token Refresh Access tokens expire after 1 hour. Implement token refresh before expiration: ```javascript class ZoomWebSocketClient { constructor(accountId, clientId, clientSecret, subscriptionId) { this.accountId = accountId; this.clientId = clientId; this.clientSecret = clientSecret; this.subscriptionId = subscriptionId; this.ws = null; this.tokenExpiry = null; } async refreshTokenIfNeeded() { const now = Date.now(); const bufferTime = 5 * 60 * 1000; // 5 minutes before expiry if (!this.tokenExpiry || now >= this.tokenExpiry - bufferTime) { const { accessToken, expiresIn } = await getAccessToken( this.accountId, this.clientId, this.clientSecret ); this.accessToken = accessToken; this.tokenExpiry = now + (expiresIn * 1000); // Reconnect with new token if (this.ws) { this.ws.close(); await this.connect(); } } } async connect() { await this.refreshTokenIfNeeded(); const wsUrl = `wss://ws.zoom.us/ws?subscriptionId=${this.subscriptionId}&access_token=${this.accessToken}`; this.ws = new WebSocket(wsUrl); // Set up event handlers... } } ``` ## Connection URL ``` wss://ws.zoom.us/ws?subscriptionId={SUBSCRIPTION_ID}&access_token={ACCESS_TOKEN} ``` | Parameter | Description | |-----------|-------------| | `subscriptionId` | Your WebSocket subscription ID from Marketplace | | `access_token` | Valid S2S OAuth access token | ## Connection Limits | Limit | Value | |-------|-------| | **Connections per subscription** | 1 (opening new connection closes existing) | | **Connection timeout** | Varies (implement keep-alive) | | **Message size** | Check Zoom docs for current limits | ## Keep-Alive / Heartbeat Maintain connection with periodic pings: ```javascript class WebSocketManager { constructor() { this.ws = null; this.pingInterval = null; } startHeartbeat() { // Ping every 30 seconds this.pingInterval = setInterval(() => { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.ping(); console.log('Ping sent'); } }, 30000); } stopHeartbeat() { if (this.pingInterval) { clearInterval(this.pingInterval); this.pingInterval = null; } } connect(url) { this.ws = new WebSocket(url); this.ws.on('open', () => { console.log('Connected'); this.startHeartbeat(); }); this.ws.on('pong', () => { console.log('Pong received - connection alive'); }); this.ws.on('close', () => { this.stopHeartbeat(); }); } } ``` ## Reconnection Strategy Implement exponential backoff for reconnection: ```javascript class ReconnectingWebSocket { constructor(config) { this.config = config; this.ws = null;