Zoom OBF implementation in MeetStream
This guide walks you through connecting your end-users’ Zoom accounts to MeetStream so bots can join their Zoom meetings using MeetStream’s hosted OBF (OAuth-based) flow. Each end-user authorizes the same Zoom Marketplace app you registered, and MeetStream stores one connection per end-user. At create-bot time you tell MeetStream which end-user the bot should act on behalf of by passing that end-user’s Zoom user id.
Production API base URL: https://api.meetstream.ai (use your environment’s host if different).
What you need
- A Zoom account with permission to create apps in the Zoom App Marketplace.
- The MeetStream User Dashboard to manage Zoom OAuth connections (the dashboard obtains the Zoom authorize URL and sends the user’s browser there).
- A deployed HTTPS server you operate at your redirect URL (for example
https://yourapp.example.com/zoom/oauth/callback). After Zoom redirects an end-user’s browser to this URL with an authorizationcode, this server must call MeetStream’s connection-create endpoint (see Step 2). MeetStream does not complete OAuth in the browser at your callback — your backend must call the connection-create endpoint. A minimal Node example lives in this repo atexamples/zoom-meetstream-oauth-callback. - The same MeetStream user API key (
Authorization: Token …) for authorize-url (used when starting each end-user’s flow) and connection-create (used on your server). The same redirect URI string everywhere: Zoom app, authorize-url, and connection-create.
Step 1: Create a Zoom app and set the redirect URL
-
Go to the Zoom App Marketplace and sign in.
-
Create a single OAuth app for your product. You only register ONE app, even if you have many end-users. Each end-user authorizes that same app under their own Zoom account.
-
In your Zoom app’s OAuth settings, add your redirect URL (for example
https://yourapp.example.com/zoom/oauth/callback). The same string must appear in:- Zoom’s OAuth Redirect URL / allow list fields (per Zoom’s UI), and
- The
redirect_uriquery parameter when MeetStream’s authorize-url API is called, and - The JSON
redirect_urifield onPOST https://api.meetstream.ai/api/v1/zoom/oauth/connections.
All three must match byte-for-byte (scheme
https, host, path, no unintended trailing slash). -
Required scopes:
user:read:user,user:read:token(so MeetStream can identify each authorizing end-user and mint OBF tokens on their behalf). -
App distribution: depends on your scenario.
- If your end-users are inside a single Zoom account (e.g. an internal tool), an account-level / internal app is fine.
- If your end-users are spread across many Zoom accounts (typical B2B SaaS), publish your app on the Zoom Marketplace so it can be installed by any Zoom account.
For development, use the same browser session for editing the Zoom app and for completing consent if Zoom or your tunnel (e.g. ngrok) relies on cookies.
Step 2: One OAuth flow per end-user (creates a Zoom OAuth Connection)
You will run this flow once per end-user. Each completed flow stores a separate connection on MeetStream, keyed by the end-user’s Zoom user id.
-
In your product’s UI, when an end-user (e.g. Alice) clicks Connect Zoom:
- Your backend calls
GET https://api.meetstream.ai/api/v1/zoom/oauth/authorize-url?redirect_uri=<https...>&state=<your-end-user-id>withAuthorization: Token <user_api_key>. - The optional
statequery parameter is round-tripped to Zoom and back to your callback URL — use it to carry your own end-user identifier (e.g.alice-internal-uuid) through the OAuth flow. - Open the returned
authorize_urlin the end-user’s browser.
- Your backend calls
-
The end-user signs in to their own Zoom account and approves the app. Zoom redirects their browser to your redirect URI with
?code=<auth_code>&state=<your-end-user-id>. -
Your server receives the request and calls MeetStream:
POST https://api.meetstream.ai/api/v1/zoom/oauth/connections- Header:
Authorization: Token <user_api_key> - Body (JSON):
- Response (200):
Save
zoom_user_idagainst the end-user record in your own DB. You will pass it back when creating bots for this end-user. - Header:
-
Repeat steps 1–3 for every additional end-user (Bob, Carol, …). Each flow uses the same Zoom Marketplace app (same
client_id/client_secret); only the authorizing end-user changes.
If anything fails, confirm the three redirect_uri strings match Zoom’s registered URL and that the same user API key is used for authorize-url and connection-create.
Listing, fetching, and deleting connections
All endpoints use Authorization: Token <user_api_key>. Token material (access_token / refresh_token) is never returned by these endpoints; only identity, metadata, state, and timestamps.
Step 3: Create a bot that joins as a specific end-user (create_bot)
When you create a bot for Alice’s meeting, pass Alice’s Zoom user id (the one returned by the connections endpoint when Alice authorized) under zoom:
meeting_linkmust be a Zoom meeting URL.use_zoom_obfmust be a boolean (trueorfalse).zoom_oauth_connection_user_idis required whenuse_zoom_obf=true. It must match thezoom_user_idreturned byPOST /api/v1/zoom/oauth/connectionsfor the end-user this bot should act on behalf of.
Do not send custom token server fields such as zoom.obf_url or zoom.zak_url; MeetStream supplies those when hosted Zoom auth is enabled.
Troubleshooting
Summary
- Zoom Marketplace: Register one HTTPS callback URL (e.g.
…/zoom/oauth/callback) for your single Zoom OAuth app. - Per end-user OAuth:
authorize-url(withstate=<your end-user id>) → end-user authorizes → your callback callsPOST /api/v1/zoom/oauth/connections→ save the returnedzoom_user_idagainst your end-user record. create_bot: Add"zoom": { "use_zoom_obf": true, "zoom_oauth_connection_user_id": "<that end-user's zoom_user_id>" }for Zoom meetings.
