Zoom OBF Token Support
Configure OBF (On Behalf Of) tokens for Zoom bots joining external meetings after March 2, 2026
Zoom OBF Token Support
Starting March 2, 2026, Zoom requires Meeting SDK applications to use On Behalf Of (OBF) tokens when joining meetings they did not create. This page explains what OBF tokens are, who is affected, and how to implement them with Meeting BaaS.
What is an OBF Token?
An OBF (On Behalf Of) token is a Zoom authorization token that proves a specific Zoom user has authorized your bot to join meetings on their behalf. It is:
- User-specific: Each token is tied to a particular Zoom user who authorized your app
- Short-lived: Tokens should be fetched close to when they are needed
- Required for external meetings: After March 2, 2026, bots cannot join meetings hosted by external accounts without an OBF token
OBF tokens require the authorized user to be present in the meeting. If they leave, the bot is disconnected. This is a Zoom requirement.
Who Needs OBF Tokens?
You need OBF tokens if:
- Your bots join Zoom meetings created by people outside your Zoom organization
- You are building a product where your customers request meeting recordings
- You use Meeting BaaS as infrastructure for a service where end users have their own Zoom accounts
You do NOT need OBF tokens if:
- Your bots only join meetings within your own Zoom account or organization
- You use your own SDK credentials (which makes all meetings "internal")
- You only use Google Meet or Microsoft Teams bots
Three Integration Options
Meeting BaaS supports three ways to provide OBF tokens, from most manual to fully automated:
Option 1: Direct Token (zoom_obf_token)
You fetch the OBF token yourself using Zoom's API and pass it directly when creating a bot.
How it works:
- Your backend calls Zoom's API to get an OBF token
- You pass the token in the bot creation request
- The bot uses this token when joining
curl -X POST "https://api.meetingbaas.com/bots" \
-H "Content-Type: application/json" \
-H "x-meeting-baas-api-key: YOUR-API-KEY" \
-d '{
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}'import requests
# First, fetch OBF token from Zoom using your stored access token
zoom_headers = {"Authorization": f"Bearer {user_access_token}"}
obf_response = requests.get(
"https://api.zoom.us/v2/users/me/token?type=onbehalf",
headers=zoom_headers
)
obf_token = obf_response.json()["token"]
# Then, create bot with the OBF token
url = "https://api.meetingbaas.com/bots"
headers = {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
}
config = {
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token": obf_token
}
response = requests.post(url, json=config, headers=headers)
print(response.json())// First, fetch OBF token from Zoom using your stored access token
const obfResponse = await fetch(
"https://api.zoom.us/v2/users/me/token?type=onbehalf",
{
headers: { Authorization: `Bearer ${userAccessToken}` },
}
);
const { token: obfToken } = await obfResponse.json();
// Then, create bot with the OBF token
const response = await fetch("https://api.meetingbaas.com/bots", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
},
body: JSON.stringify({
meeting_url: "https://zoom.us/j/123456789",
bot_name: "Recording Bot",
zoom_obf_token: obfToken,
}),
});
console.log(await response.json());Pros:
- Full control over token lifecycle
- No credentials stored in Meeting BaaS
Cons:
- You must implement OAuth token storage and refresh
- Token may expire if there is delay between fetching and bot joining
- Must fetch a fresh token for each bot request
Best for: Testing, debugging, or customers who already have Zoom OAuth infrastructure.
Option 2: Token URL (zoom_obf_token_url)
You provide a URL that returns an OBF token. The bot calls this URL at join time to fetch a fresh token.
How it works:
- You host an endpoint that returns OBF tokens
- You pass the endpoint URL when creating a bot
- The bot calls your endpoint at join time and receives the token
curl -X POST "https://api.meetingbaas.com/bots" \
-H "Content-Type: application/json" \
-H "x-meeting-baas-api-key: YOUR-API-KEY" \
-d '{
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token_url": "https://your-api.com/zoom/obf-token?user_id=abc123"
}'import requests
url = "https://api.meetingbaas.com/bots"
headers = {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
}
config = {
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token_url": "https://your-api.com/zoom/obf-token?user_id=abc123"
}
response = requests.post(url, json=config, headers=headers)
print(response.json())fetch("https://api.meetingbaas.com/bots", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
},
body: JSON.stringify({
meeting_url: "https://zoom.us/j/123456789",
bot_name: "Recording Bot",
zoom_obf_token_url: "https://your-api.com/zoom/obf-token?user_id=abc123",
}),
})
.then((response) => response.json())
.then((data) => console.log(data));Your endpoint must:
- Accept a GET request
- Return the raw OBF token as the response body (plain text, not JSON)
- Handle OAuth token refresh internally
- Be accessible from Meeting BaaS infrastructure
Parameters passed to your endpoint:
When the bot calls your endpoint, it appends these query parameters:
| Parameter | Description |
|---|---|
bot_uuid | The Meeting BaaS bot UUID for this request |
extra | JSON string of any extra data you passed when creating the bot |
For example, if you create a bot with:
{
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token_url": "https://your-api.com/zoom/obf-token",
"extra": {"user_id": "usr_456", "org_id": "org_789"}
}Your endpoint will receive:
GET https://your-api.com/zoom/obf-token?bot_uuid=abc-123-def&extra={"user_id":"usr_456","org_id":"org_789"}This lets you use either our bot_uuid or your own identifiers in extra to look up which token to return.
Example endpoint implementation:
from flask import Flask, request
import requests
import json
app = Flask(__name__)
@app.route("/zoom/obf-token")
def get_obf_token():
# Option 1: Use bot_uuid to look up the user
bot_uuid = request.args.get("bot_uuid")
# Option 2: Use your own identifiers from extra
extra_str = request.args.get("extra")
if extra_str:
extra = json.loads(extra_str)
user_id = extra.get("user_id")
# Look up stored OAuth credentials for this user
access_token = get_stored_access_token(user_id)
# Refresh if expired
if is_expired(access_token):
access_token = refresh_access_token(user_id)
# Fetch OBF token from Zoom
response = requests.get(
"https://api.zoom.us/v2/users/me/token?type=onbehalf",
headers={"Authorization": f"Bearer {access_token}"}
)
return response.json()["token"]Pros:
- Token is always fresh (fetched at join time)
- You maintain full control of OAuth credentials
Cons:
- You must host and maintain an endpoint
- You must implement OAuth token storage and refresh
Best for: Customers who want to keep Zoom credentials on their own infrastructure.
Option 3: Managed OAuth (zoom_obf_token_user_id)
Meeting BaaS stores the OAuth credentials and handles token refresh automatically. This is the recommended option for most customers.
How it works:
- Your Zoom user goes through OAuth consent flow
- You send the authorization code to Meeting BaaS
- Meeting BaaS stores the tokens and refreshes them automatically
- When creating a bot, you just specify the Zoom user ID
- Meeting BaaS fetches a fresh OBF token at join time
Step 1: Set Up OAuth Consent Flow
Direct your Zoom user to the OAuth authorization URL:
https://zoom.us/oauth/authorize?response_type=code&client_id={YOUR_CLIENT_ID}&redirect_uri={YOUR_REDIRECT_URI}After the user authorizes, Zoom redirects to your redirect URI with an authorization code:
{YOUR_REDIRECT_URI}?code=AUTHORIZATION_CODEStep 2: Create Zoom OAuth Connection
Send the authorization code to Meeting BaaS:
curl -X POST "https://api.meetingbaas.com/zoom_oauth_connections" \
-H "Content-Type: application/json" \
-H "x-meeting-baas-api-key: YOUR-API-KEY" \
-d '{
"authorization_code": "AUTHORIZATION_CODE_FROM_ZOOM",
"redirect_uri": "https://your-app.com/oauth/callback",
"zoom_client_id": "YOUR_ZOOM_CLIENT_ID",
"zoom_client_secret": "YOUR_ZOOM_CLIENT_SECRET"
}'import requests
url = "https://api.meetingbaas.com/zoom_oauth_connections"
headers = {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
}
data = {
"authorization_code": "AUTHORIZATION_CODE_FROM_ZOOM",
"redirect_uri": "https://your-app.com/oauth/callback",
"zoom_client_id": "YOUR_ZOOM_CLIENT_ID",
"zoom_client_secret": "YOUR_ZOOM_CLIENT_SECRET"
}
response = requests.post(url, json=data, headers=headers)
print(response.json())const response = await fetch(
"https://api.meetingbaas.com/zoom_oauth_connections",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
},
body: JSON.stringify({
authorization_code: "AUTHORIZATION_CODE_FROM_ZOOM",
redirect_uri: "https://your-app.com/oauth/callback",
zoom_client_id: "YOUR_ZOOM_CLIENT_ID",
zoom_client_secret: "YOUR_ZOOM_CLIENT_SECRET",
}),
}
);
const connection = await response.json();
console.log(connection);
// { uuid: "...", zoom_user_id: "SeJwoMGwTCu52501SbDC0Q", state: "connected", ... }Response:
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"zoom_user_id": "SeJwoMGwTCu52501SbDC0Q",
"zoom_account_id": "AplWZ5oMSouJOw9zu0cmKQ",
"state": "connected",
"scopes": "user:read:token user:read:user",
"created_at": "2026-02-08T16:00:00",
"updated_at": "2026-02-08T16:00:00"
}Save the zoom_user_id from the response. You will use this when creating bots.
Step 3: Create Bots with Managed OBF
curl -X POST "https://api.meetingbaas.com/bots" \
-H "Content-Type: application/json" \
-H "x-meeting-baas-api-key: YOUR-API-KEY" \
-d '{
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token_user_id": "SeJwoMGwTCu52501SbDC0Q"
}'import requests
url = "https://api.meetingbaas.com/bots"
headers = {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
}
config = {
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token_user_id": "SeJwoMGwTCu52501SbDC0Q"
}
response = requests.post(url, json=config, headers=headers)
print(response.json())fetch("https://api.meetingbaas.com/bots", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-meeting-baas-api-key": "YOUR-API-KEY",
},
body: JSON.stringify({
meeting_url: "https://zoom.us/j/123456789",
bot_name: "Recording Bot",
zoom_obf_token_user_id: "SeJwoMGwTCu52501SbDC0Q",
}),
})
.then((response) => response.json())
.then((data) => console.log(data));Meeting BaaS will automatically:
- Look up the stored OAuth connection for this Zoom user
- Refresh the access token if expired
- Fetch a fresh OBF token from Zoom's API
- Pass it to the bot at join time
Pros:
- Fully automated after initial setup
- No need to manage tokens yourself
- Token is always fresh
- Handles refresh automatically
Cons:
- Zoom OAuth credentials must be shared with Meeting BaaS
- Requires building an OAuth consent UI for your users
Best for: Most customers, especially those building products for end users.
App Attribution (Active Apps Notifier)
When using OBF tokens, the bot's SDK credentials determine which app name appears in Zoom's Active Apps Notifier (AAN) — the notice shown to all meeting participants identifying which app is accessing meeting content.
By default, if you only pass an OBF token without your own SDK credentials, the bot uses Meeting BaaS's default SDK credentials and the AAN will display "Meeting Baas" instead of your app name.
To show your app name in the AAN, pass your SDK credentials alongside your OBF token option:
{
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_sdk_id": "YOUR_SDK_KEY",
"zoom_sdk_pwd": "YOUR_SDK_SECRET",
"zoom_obf_token_url": "https://your-api.com/zoom/obf-token"
}This works with all three OBF options. The SDK credentials and OBF tokens serve different purposes — SDK credentials control the app identity (AAN), while OBF tokens authorize the bot to join on behalf of a user.
Zoom Marketplace Requirement: During Marketplace review, Zoom requires the AAN to display your app's name. If you're going through review, the reviewer may flag this if your SDK credentials are not included. See Zoom App Setup for details.
Migrating to v2? In the v2 API, you can store your SDK credentials once using the Credentials API instead of passing them with every request. This is more secure and simplifies your integration.
Bot Behavior with OBF Tokens
Authorized User Not in Meeting
When the bot joins with an OBF token and the authorized user is not yet in the meeting:
- Bot attempts to join
- Zoom returns "Authorized user not in meeting" error
- Bot retries every 30 seconds
- Once the authorized user joins, the bot successfully enters
- If the user does not join within the timeout period, the bot exits with error
The timeout is controlled by automatic_leave.waiting_room_timeout (default: 200 seconds).
{
"meeting_url": "https://zoom.us/j/123456789",
"bot_name": "Recording Bot",
"zoom_obf_token_user_id": "SeJwoMGwTCu52501SbDC0Q",
"automatic_leave": {
"waiting_room_timeout": 300
}
}Authorized User Leaves
If the authorized user leaves the meeting while the bot is active, Zoom ends the SDK session. The bot will:
- Stop recording
- Upload any recorded content
- Send completion webhook
- Exit the meeting
This is a Zoom requirement and cannot be changed.
Error Codes
| Error Code | Meaning |
|---|---|
WaitingForHostTimeout | The authorized user did not join the meeting within the timeout period |
CannotJoinMeeting | Generic join failure. With OBF, this could mean an invalid or expired token |
ZOOM_SDK_AUTH_FAILED | SDK authentication failed. Check SDK credentials or OBF token validity |
API Reference
Zoom OAuth Connections
The following endpoints manage Zoom OAuth connections for the managed OBF flow:
POST /zoom_oauth_connections— Create a new connection by exchanging an authorization codeGET /zoom_oauth_connections— List all connections for your accountGET /zoom_oauth_connections/:uuid— Get a specific connectionDELETE /zoom_oauth_connections/:uuid— Delete a connection and remove stored tokens
See the API Reference for full endpoint documentation.
Bot Parameters
| Parameter | Type | Description |
|---|---|---|
zoom_obf_token | string | Raw OBF token (Option 1) |
zoom_obf_token_url | string | URL that returns an OBF token (Option 2) |
zoom_obf_token_user_id | string | Zoom user ID from a stored OAuth connection (Option 3) |
Only provide one of these parameters. If multiple are provided, precedence is: zoom_obf_token > zoom_obf_token_url > zoom_obf_token_user_id.
Migration Checklist
Determine if You Need OBF Tokens
Do your bots join meetings hosted by external Zoom accounts? If yes, you need OBF tokens. If your bots only join meetings within your own Zoom organization, consider using SDK credentials instead.
Create or Update Your Zoom App
Ensure your Zoom app has the user:read:token and user:read:user scopes. See Zoom App Setup.
Choose an Integration Option
- Option 1 (Direct Token): For testing or if you already have OAuth infrastructure
- Option 2 (Token URL): If you want to keep credentials on your infrastructure
- Option 3 (Managed OAuth): Recommended for most customers
Implement OAuth Consent Flow
For Options 2 and 3, you need to implement a way for Zoom users to authorize your app (e.g. a "Connect Zoom" button). For Option 1 (Direct Token), you still need a way to obtain a Zoom access token—for example, a one-time OAuth flow or script—so you can call Zoom's token API to fetch the OBF token.
Update Bot Creation Requests
Add the appropriate OBF parameter (zoom_obf_token, zoom_obf_token_url, or zoom_obf_token_user_id) to your bot creation requests.
Test Before March 2, 2026
Test your integration with real Zoom meetings before the enforcement date.
FAQ
Q: What happens if I do not implement OBF tokens by March 2, 2026?
Your bots will fail to join external Zoom meetings. They will receive a join failure error.
Q: Can I use one OBF token for multiple meetings?
Yes, OBF tokens are not meeting-specific by default. When fetched via the API without specifying a meeting number, the token is valid for all meetings.
Q: Do I need OBF tokens for Google Meet or Microsoft Teams?
No, OBF tokens are a Zoom-specific requirement.
Q: What if the authorized user's Zoom account is deactivated?
The OAuth connection will become invalid. The user would need to re-authorize your app.
Q: Is there an alternative to OBF tokens for continuous recording?
Zoom is developing Real-Time Media Streams (RTMS) for continuous recording use cases. We are working on RTMS support, but it has different constraints (runs as an app inside the meeting, no bidirectional streaming support yet).
