> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.meetstream.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.meetstream.ai/_mcp/server.

# Zoom Meeting Bots

> Run meeting bots on Zoom with the MeetStream API: one-time Marketplace app setup, hosted OAuth (OBF) for end users, recording permission flow, per-participant audio and video, and Zoom-specific timeouts.

MeetStream bots join Zoom meetings through the Zoom Meeting SDK. Zoom is the one platform that needs a **one-time setup** (Google Meet and Microsoft Teams work with zero configuration) — after that, sending a bot is the same single `create_bot` call as everywhere else.

```bash
curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \
  -H "Authorization: Token <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "meeting_link": "https://zoom.us/j/123456789",
    "bot_name": "Notetaker"
  }'
```

## One-time setup

1. Create a **General App** in the [Zoom App Marketplace](https://marketplace.zoom.us) (Develop → Build App → General App). User-managed or admin-managed both work.
2. Set the OAuth Redirect URL to `https://api.meetstream.ai/api/v1/admin/zoom/oauth/callback` and add `https://meetstream.ai` to the OAuth Allow List.
3. Under Features → Embed, enable **Meeting SDK** (leave Device OAuth off).
4. Copy the Client ID and Client Secret into the MeetStream Dashboard → **Integrations** → Zoom, and complete the OAuth authorization.

Full walkthrough: [Zoom Marketplace App Setup](/guides/app-integrations/zoom-marketplace-app-setup).

**Development-mode limitation:** with development credentials your bot can only join meetings hosted by your own Zoom account. To join anyone's meetings, submit the app for production approval — see [Zoom App Production Submission](/guides/app-integrations/zoom-app-production-submission). Review typically takes a few business days.

## Joining as your end users (OBF / hosted OAuth)

If you're building a product where *your* customers connect their own Zoom accounts, use MeetStream's hosted On-Behalf-Of flow. You register **one** Zoom OAuth app; each end user authorizes it once, and MeetStream stores one connection per end user keyed by their Zoom user ID.

```json
{
  "meeting_link": "https://zoom.us/j/123456789?pwd=...",
  "bot_name": "Notetaker",
  "zoom": {
    "use_zoom_obf": true,
    "zoom_oauth_connection_user_id": "<zoom_user_id>"
  }
}
```

Connection management endpoints (`/api/v1/zoom/oauth/...`) never return token material — only identity and connection state. Full flow, required scopes (`user:read:user`, `user:read:token`), and error handling: [Zoom OBF Implementation](/guides/app-integrations/zoom-obf-implementation).

## The recording permission flow

Zoom is the only platform where recording is gated on **host consent**. After `bot.inmeeting`, the bot requests recording permission and waits — so there can be a visible gap before `bot.recording` (on Meet and Teams it fires within about a second).

* Host grants → `bot.recording_permission_allowed` → `bot.recording`
* Host denies, or doesn't respond within `recording_permission_denied_timeout` → `bot.recording_permission_denied` → `bot.leaving` → `bot.stopped` (a clean stop, not `bot.denied`)

`recording_permission_denied_timeout` accepts 60–300 seconds (default 60) and is ignored on the other platforms.

## Zoom-specific behavior at a glance

| Behavior                             | On Zoom                                                                                                                                                                                          |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Setup                                | One-time Marketplace app + dashboard credentials                                                                                                                                                 |
| Recording start                      | Gated on host permission (see above)                                                                                                                                                             |
| `waiting_room_timeout`               | 60–1200s, default 600 → `bot.notallowed` if never admitted                                                                                                                                       |
| Per-participant audio                | **Full isolation** — the Zoom SDK provides a dedicated raw PCM stream per participant, so each file contains only that speaker's microphone. The cleanest per-participant files of any platform. |
| Per-participant video                | Webcam 640×360 fixed @ 300 kbps; screen share at **native resolution** @ 1.5 Mbps; 1 concurrent screen share                                                                                     |
| Screenshots endpoint                 | Not supported on Zoom bots                                                                                                                                                                       |
| Native captions (`meeting_captions`) | Not available on Zoom — use any other [transcription provider](/guides/transcription-recordings/post-call-transcription)                                                                         |
| Chat + images                        | `send_message` and `send_image` inject chat and visuals into the live meeting                                                                                                                    |

## Troubleshooting joins

The usual causes when a Zoom bot fails to join:

* Invalid or expired meeting link
* App still in **development mode** and the meeting isn't hosted by your account
* Bot stuck in the waiting room until `waiting_room_timeout` fired (`bot.notallowed`)
* Password-protected meeting link missing its `?pwd=` component
* OBF: the end user's connection was revoked (password change, app uninstall, or 90+ days idle) — they need to reconnect

Watch it live with [`meetstream listen`](/build-with-ai/meetstream-cli) or the [Debugging guide](/guides/help/debugging-bots).

## Next steps

* [Create your first bot](/guides/get-started/create-your-first-bot)
* [Per-participant audio](/guides/transcription-recordings/per-participant-audio) and [video](/guides/transcription-recordings/per-participant-video)
* [Webhooks and events](/guides/webhooks/webhooks-and-events)