Interactive demonstration of all 4 Zoom OAuth authorization flows
┌─────────────────────────────┐
│ What are you building? │
└──────────────┬──────────────┘
│
┌─────────────────────────────────┼─────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────────────┐
│ Backend automation │ │ App for other users │ │ Chatbot only │
│ (your own account) │ │ or other accounts │ │ (Team Chat) │
│ │ │ │ │ │
│ Examples: │ │ Examples: │ │ Examples: │
│ • Scheduled reports │ │ • SaaS integrations │ │ • Slash commands │
│ • Auto-provisioning │ │ • Meeting schedulers │ │ • Notifications │
│ • Data sync │ │ • Recording managers │ │ • Interactive cards │
└───────────┬────────────┘ └───────────┬────────────┘ └───────────┬────────────┘
│ │ │
▼ │ ▼
┌────────────────────────┐ │ ┌────────────────────────┐
│ ■ S2S OAUTH │ │ │ ■ CLIENT (CHATBOT) │
│ account_credentials │ │ │ client_credentials │
│ │ │ │ │
│ No user interaction │ │ │ Scope: imchat:bot │
│ Token: 1 hour │ │ │ Token: 1 hour │
│ No refresh needed │ │ │ No refresh needed │
└────────────────────────┘ │ └────────────────────────┘
│
▼
┌──────────────────────────────┐
│ Does the device have a │
│ browser for login? │
└──────────────┬───────────────┘
│
┌────────────────────┴────────────────────┐
│ NO YES│
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────┐
│ ■ DEVICE FLOW │ │ ■ USER AUTHORIZATION │
│ device_code │ │ authorization_code │
│ │ │ │
│ Examples: │ │ + Add PKCE if: │
│ • Smart TV apps │ │ • Mobile app │
│ • Meeting SDK devices │ │ • Single Page App (SPA) │
│ • Kiosk displays │ │ • Desktop app │
│ • CLI tools │ │ │
│ │ │ Token: 1 hour │
│ User visits URL on phone │ │ Refresh: 90 days │
│ Token: 1 hour │ │ │
│ Refresh: 90 days │ │ │
└────────────────────────────┘ └────────────────────────────┘
| Criteria | S2S OAuth | User Auth | Device Flow | Chatbot |
|---|---|---|---|---|
| Grant Type | account_credentials |
authorization_code |
urn:ietf:params:oauth:grant-type:device_code |
client_credentials |
| User Interaction | None | Browser redirect | Enter code on separate device | None |
| App Type | Server-to-Server | General App | General App | General App (with chatbot) |
| Token Lifetime | 1 hour | 1 hour | 1 hour | 1 hour |
| Refresh Token | No (request new) | Yes (90 days) | Yes (90 days) | No (request new) |
| Scope Access | Account-wide | User or Account (if admin) | User-level | imchat:bot only |
| Best For | Backend automation, scripts, cron jobs | Web apps, user-facing integrations | TV apps, CLI, kiosks, Meeting SDK | Team Chat bots |
| Security Model | Confidential (server-side secrets) | Confidential or Public (+PKCE) | Confidential (server validates) | Confidential (server-side) |
No user involved. Direct client-to-server authentication.
Zoom flows: S2S OAuth, Chatbot
User is involved. User authorizes app to act on their behalf.
Zoom flows: User Auth, Device Flow
Automated backend services with no human interaction.
Zoom flows: S2S OAuth
Security extension for public clients (mobile, SPA). Prevents authorization code interception.
Zoom flows: User Auth (optional)
For backend automation without user interaction. Uses grant_type=account_credentials.
For apps that act on behalf of users. Uses grant_type=authorization_code.
For devices without browsers. Uses grant_type=urn:ietf:params:oauth:grant-type:device_code.
For Team Chat chatbot operations. Uses grant_type=client_credentials.
imchat:bot scope. Used for sending chatbot messages.
┌─────────────────────────────────────────────────────────────────────────────────────────┐ │ TOKEN LIFECYCLE BY FLOW │ └─────────────────────────────────────────────────────────────────────────────────────────┘ ■ S2S OAuth / Chatbot (No Refresh) ┌──────────┐ POST /oauth/token ┌──────────┐ 1 hour ┌──────────┐ │ Start │ ─────────────────────────► │ Token │ ────────────► │ Expired │ └──────────┘ account_credentials │ Valid │ └────┬─────┘ ▲ or client_credentials └──────────┘ │ │ │ └──────────────────── Request new token ───────────────────────────┘ ■ User Auth / Device Flow (With Refresh) ┌──────────┐ Authorization ┌──────────┐ POST /oauth/token ┌──────────┐ │ Start │ ─────────────────────► │ Code │ ────────────────────────► │ Tokens │ └──────────┘ (code or device) │ Received │ authorization_code │ Received │ └──────────┘ or device_code └────┬─────┘ │ ┌──────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────┐ ┌─────────────┐ │ Access Token│◄──── Refresh ─────│ Refresh │ │ (1 hour) │ Token │ Token │ └──────┬──────┘ │ (90 days) │ │ └──────┬──────┘ │ Expires │ Expires ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ Use │ │ Re-auth │ │ Refresh │ │ Required │ │ Token │ │ (new flow) │ └─────────────┘ └─────────────┘
| Error Code | Message | Solution |
|---|---|---|
4700 |
Token cannot be empty | Check Authorization header has valid token |
4702/4704 |
Invalid client | Verify Client ID and Client Secret |
4705 |
Grant type not supported | Use: account_credentials, authorization_code, device_code, or client_credentials |
4709 |
Redirect URI mismatch | Ensure redirect_uri matches app configuration exactly |
4733 |
Code is expired | Authorization codes expire in 5 minutes - restart flow |
4741 |
Token has been revoked | Use the most recent token from latest authorization |
Zoom OAuth Flows Demo | Official Documentation