This Node.js application demonstrates how to manually start and stop Zoom's Realtime Media Streaming (RTMS).
- Listens for Zoom webhook events related to RTMS.
- Connects to signaling and media WebSocket servers.
- Captures and processes realtime transcript data.
- Supports multiple media types: audio, video, transcript, and chat.
- Includes automatic RTMS stop functionality after a configurable duration.
Create a .env file with the following keys:
ZOOM_CLIENT_ID=your_zoom_client_id
ZOOM_CLIENT_SECRET=your_zoom_client_secret
WEBHOOK_PATH=/webhook
access_token=your_zoom_access_token
PORT=3000
# Optional: start/stop RTMS for a specific invitee already in the meeting.
# Omit to use the user represented by access_token.
RTMS_PARTICIPANT_USER_ID=zoom_user_id- Validates Zoom webhook events for meeting lifecycle.
- WebSocket handshakes secured with HMAC SHA256 signatures.
- Realtime transcript stream capture from Zoom meetings.
- Automatic RTMS stop scheduling after 10 seconds (configurable).
- Handles keep-alive requests and maintains connection health.
- Automatically cleans up connections when meetings end.
- Optionally targets a specific joined invitee with
RTMS_PARTICIPANT_USER_ID.
POST /webhook– Handles Zoom RTMS webhook events.
meeting.started– Initiates RTMS start and schedules automatic stopmeeting.rtms_started– Connects to signaling WebSocketmeeting.rtms_stopped– Logs meeting stop event
The application listens for Zoom webhook events on /webhook endpoint.
When meeting.started is received:
- Step 2a: Listen to meeting started event
- Step 2b: Get access token from environment. To implement complete logicfor authorization, please visit here
- Step 2c: Make API call to start RTMS using the meeting ID from the webhook.
If RTMS_PARTICIPANT_USER_ID is set, the request includes
settings.participant_user_id. The participant must be an invitee who has
already joined the meeting. Without it, Zoom uses the user represented by the
access token.
When meeting.rtms_started is received, the application connects to Zoom's signaling WebSocket.
Creates HMAC SHA256 signature for authentication handshake using client ID, meeting UUID, and stream ID.
- Connects to Zoom's signaling WebSocket server
- Sends handshake request with meeting details and signature
- Receives media server URL upon successful handshake
- Connects to media WebSocket server
- Sends media handshake requesting transcript stream
- Receives and processes real-time transcript data
Automatically stops RTMS after 10 seconds using Zoom API.
msg_type: 1– HANDSHAKE_REQUESTmsg_type: 2– HANDSHAKE_RESPONSEmsg_type: 3– MEDIA_HANDSHAKE_REQUESTmsg_type: 4– MEDIA_HANDSHAKE_RESPONSEmsg_type: 5– TRANSCRIPT_DATAmsg_type: 7– CLIENT_READY_ACKmsg_type: 12– KEEP_ALIVE_REQUESTmsg_type: 13– KEEP_ALIVE_RESPONSE
The REST start/stop request uses PATCH /live_meetings/{meetingId}/rtms_app/status.
The access token needs the appropriate RTMS status scope, such as
meeting:write or the corresponding admin/participant granular scope.
- Node.js v14 or higher
- A valid Zoom App with webhook events and meeting and RTMS scopes.
- Ngrok or another tunneling service for public webhook exposure
npm installnode rtms.jsThe server will start on port 3000 and listen for webhook events.
- RTMS is automatically stopped after 10 seconds to demo the stop action of the API
- All WebSocket connections are properly closed when meetings end
- Transcript data is logged to console for debugging
Normal Zoom webhook deliveries are verified against the exact raw request body using
x-zm-signature and x-zm-request-timestamp. Configure ZOOM_SECRET_TOKEN with the
Marketplace app's webhook Secret Token. Requests with missing, invalid, or stale
signatures are rejected; the default replay window is 300 seconds and can be changed
with WEBHOOK_TIMESTAMP_TOLERANCE_SECONDS.