MeetStream Guide: Zoom Meeting Direct Connect (RTMS)
Record and transcribe a Zoom meeting without a bot joining it. Meeting Direct Connect uses Zoom’s Realtime Media Streams (RTMS): Zoom streams the meeting’s audio, video, screen share, transcript and participant events to MeetStream through your own Zoom app. No “notetaker” tile appears in the meeting, and there is no waiting room or recording-permission prompt.
A Direct Connect session is a normal MeetStream bot with platform: "ZoomRTMS". Every /api/v1/bots/{bot_id}/... endpoint you already use (status, audio, video, transcripts, participants, speaker timeline) works with its ID, and you receive the same signed webhooks.
How it works
- A meeting starts and RTMS starts for your Zoom app. The host can start it, or your Zoom account can start it automatically.
- Zoom sends a
meeting.rtms_startedwebhook. - MeetStream connects to the stream. There are two ways this happens:
- Option A: you forward the webhook. Your server receives Zoom’s webhook, signs it, and calls
POST /api/v1/meeting_direct_connect. Your Zoom client secret never leaves your server. - Option B: MeetStream receives the webhook. You register your Zoom app with MeetStream once and point the app’s Event Subscription at MeetStream. You don’t need your own server.
- Option A: you forward the webhook. Your server receives Zoom’s webhook, signs it, and calls
- MeetStream records until Zoom stops the stream, you call
leave, or the maximum session length is reached. The usual post-call processing then runs, and you getbot.done.
Prerequisites
- A Zoom General app with RTMS enabled, and these event subscriptions:
meeting.rtms_startedmeeting.rtms_stopped
- RTMS scopes on the app for the media you want, for example
meeting:read:meeting_audio,meeting:read:meeting_videoandmeeting:read:meeting_transcript. - RTMS turned on for the meeting. Either the host starts it, or an account setting starts it automatically. Starting RTMS needs Zoom’s Developer Pack on the app owner’s Zoom account.
- Your MeetStream API key.
Option A: Forward Zoom’s webhook
Endpoint
zoom_rtms fields:
Optional fields
These fields are not accepted, because no bot joins the meeting: meeting_link, join_at, socket_connection_url, agents, video_layout, bot_message, bot_image_url and streaming transcription providers.
Example request
Response (201)
id and bot_id are the same value. Use it with any /api/v1/bots/{bot_id}/... endpoint.
Example: forwarding server (Python, Flask)
Example: forwarding server (Node.js, Express)
Answer Zoom within 3 seconds. If Zoom doesn’t get a quick 200, it retries the webhook. MeetStream rejects a duplicate for a stream that is already active with 409, so a retry never starts a second session.
Option B: Let MeetStream receive Zoom’s webhook
Register your Zoom app once. This is the same app registry used for Zoom OAuth bots (see the Zoom OAuth Meeting Bots guide). Add an rtms block with the defaults for every session the app starts:
The response includes zoom_oauth_app_id and webhook_url. In your Zoom app, set the Event Subscription URL to that webhook_url and subscribe to meeting.rtms_started and meeting.rtms_stopped.
From then on, MeetStream checks Zoom’s signature on each webhook and starts a session for every meeting.rtms_started. Each session’s custom_attributes include zoom_oauth_app_id, zoom_meeting_id and zoom_operator_id, so you can match it to your meeting.
- Change the defaults:
PATCH /api/v1/zoom/oauth/apps/{zoom_oauth_app_id}with a newrtmsblock. - Stop starting sessions for the app:
PATCHwith{"rtms": {"enabled": false}}.
The rtms block accepts enabled, bot_name, callback_url, custom_attributes, recording_config, live_transcription_required, live_audio_required, live_video_required, video_required, video_separate_streams, audio_separate_streams, audio_separate_mp3 and automatic_leave. callback_url must be an https:// URL.
Get a session
Returns the session’s status, its status history, and its recordings with download links:
video_mixed appears only when the session was started with video_required: true. It shows processing until the video is ready.
To list all sessions, call GET /api/v1/bots?platform=ZoomRTMS.
End a session early
This stops recording and starts post-call processing. It works the same as POST /api/v1/bots/{bot_id}/remove_bot. Calling it on a session that has already finished returns 200 and keeps its final status.
A session also ends when the host stops RTMS, when the meeting ends, or when in_call_recording_timeout is reached.
Webhooks
Sessions send the same signed webhooks as bots (bot.inmeeting, bot.recording, bot.leaving, bot.stopped, bot.done and failure events) to your callback_url. participant_events.join and participant_events.leave are available as real-time events. Verify them the same way as other MeetStream webhooks (see the Webhook Signature Verification guide).
Limitations
- Receive-only. Nothing can be sent into the meeting: no chat messages, images, audio playback, raise hand or MIA agents.
send_message,send_image,send_command,pause_recordingandresume_recordingreturn400withcode: "unsupported_on_zoom_rtms". - No chat capture yet.
get_chatsreturns an empty list. - Participant events are join and leave only. Microphone, camera, host and email details are not available.
- Video is the active speaker. Zoom streams one camera at a time, so
video_separate_streamsgives each participant’s video only while they are the active speaker. - Per-participant audio covers up to 3 people speaking at the same time, a Zoom limit.
- Live transcription uses Zoom’s own transcript (
meeting_captions). Streaming transcription providers are not supported. - No breakout rooms. RTMS streams the main meeting only.
- The host stays in control. A Zoom admin can turn off RTMS during a meeting, and the session then ends.
- Start-up time. A session can take up to about a minute to start receiving media.
- US execution region only.
Error Responses (POST /api/v1/meeting_direct_connect)
If the signature is wrong, the request is still accepted (201), but the session moves to fatal when Zoom rejects the connection. Check GET /api/v1/meeting_direct_connect/{id} or the failure webhook.
