zoom-oauth
Reference skill for Zoom authentication. Use after routing to an auth workflow when choosing app credentials, grant types, scopes, token refresh behavior, or debugging Zoom OAuth failures.
- License 许可
- Apache-2.0
- 最近更新
- 2026-11-13
- Stars
- ★ 3.6w
- Forks
- 3.9k
01 前言 Front Matter
- name
- zoom-oauth
- triggers
- [ "zoom oauth", "zoom authentication", "zoom authorization", "server to server oauth", "s2s oauth", "zoom access token", "zoom refresh token", "authorization code flow", "device authorization", "pkce", "zoom api authentication", "oauth error 4709", "oauth error 4733", "oauth error 4735", "redirect uri mismatch" ]
- description
- Reference skill for Zoom authentication. Use after routing to an auth workflow when choosing app credentials, grant types, scopes, token refresh behavior, or debugging Zoom OAuth failures.
- user-invocable
- false
02 资源文件 21
- 参考文档concepts/oauth-flows.md22.2 KB
- 参考文档concepts/pkce.md13.8 KB
- 参考文档concepts/scopes-architecture.md6.7 KB
- 参考文档concepts/state-parameter.md8.1 KB
- 参考文档concepts/token-lifecycle.md15.1 KB
- 参考文档examples/device-flow.md371 B
- 参考文档examples/pkce-implementation.md379 B
- 参考文档examples/s2s-oauth-basic.md375 B
- 参考文档examples/s2s-oauth-redis.md6.6 KB
- 参考文档examples/token-refresh.md373 B
- 参考文档examples/user-oauth-basic.md376 B
- 参考文档examples/user-oauth-mysql.md376 B
- 参考文档references/classic-scopes.md362.3 KB
- 参考文档references/environment-variables.md863 B
- 参考文档references/granular-scopes.md368.4 KB
- 参考文档references/oauth-errors.md4.8 KB
- 参考文档RUNBOOK.md3.3 KB
- 参考文档troubleshooting/common-errors.md458 B
- 参考文档troubleshooting/redirect-uri-issues.md229 B
- 参考文档troubleshooting/scope-issues.md222 B
- 参考文档troubleshooting/token-issues.md222 B
03 SKILL.md 正文
Zoom OAuth
Background reference for Zoom auth and token lifecycle behavior. Prefer setup-zoom-oauth first, then use this skill for the exact flow, scope, and error details.
Zoom OAuth
Authentication and authorization for Zoom APIs.
📖 Complete Documentation
For comprehensive guides, production patterns, and troubleshooting, see Integrated Index section below.
Quick navigation:
- 5-Minute Runbook - Preflight checks before deep debugging
- OAuth Flows - Which flow to use and how each works
- Token Lifecycle - Expiration, refresh, and revocation
- Production Examples - Redis caching, MySQL storage, auto-refresh
- Troubleshooting - Error codes 4700-4741
Prerequisites
- Zoom app created in Marketplace
- Client ID and Client Secret
- For S2S OAuth: Account ID
Four Authorization Use Cases
| Use Case | App Type | Grant Type | Industry Name |
|---|---|---|---|
| Account Authorization | Server-to-Server | account_credentials |
Client Credentials Grant, M2M, Two-legged OAuth |
| User Authorization | General | authorization_code |
Authorization Code Grant, Three-legged OAuth |
| Device Authorization | General | urn:ietf:params:oauth:grant-type:device_code |
Device Authorization Grant (RFC 8628) |
| Client Authorization | General | client_credentials |
Client Credentials Grant (chatbot-scoped) |
Industry Terminology
| Term | Meaning |
|---|---|
| Two-legged OAuth | No user involved (client ↔ server) |
| Three-legged OAuth | User involved (user ↔ client ↔ server) |
| M2M | Machine-to-Machine (backend services) |
| Public client | Can't keep secrets (mobile, SPA) → use PKCE |
| Confidential client | Can keep secrets (backend servers) |
| PKCE | Proof Key for Code Exchange (RFC 7636), pronounced "pixy" |
Which Flow Should I Use?
┌─────────────────────┐
│ What are you │
│ building? │
└──────────┬──────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Backend │ │ App for other │ │ Chatbot only │
│ automation │ │ users/accounts │ │ (Team Chat) │
│ (your account) │ │ │ │ │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
▼ │ ▼
┌─────────────────┐ │ ┌─────────────────┐
│ ACCOUNT │ │ │ CLIENT │
│ (S2S OAuth) │ │ │ (Chatbot) │
└─────────────────┘ │ └─────────────────┘
│
▼
┌─────────────────────┐
│ Does device have │
│ a browser? │
└──────────┬──────────┘
│
┌───────────────┴───────────────┐
│ NO YES│
▼ ▼
┌─────────────────────────┐ ┌─────────────────┐
│ DEVICE │ │ USER │
│ (Device Flow) │ │ (Auth Code) │
│ │ │ │
│ Examples: │ │ + PKCE if │
│ • Smart TV │ │ public client │
│ • Meeting SDK device │ │ │
└─────────────────────────┘ └─────────────────┘
Account Authorization (Server-to-Server OAuth)
For backend automation without user interaction.
Request Access Token
POST https://zoom.us/oauth/token?grant_type=account_credentials&account_id={ACCOUNT_ID}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
Response
{
"access_token": "eyJ...",
"token_type": "bearer",
"expires_in": 3600,
"scope": "user:read:user:admin",
"api_url": "https://api.zoom.us"
}
Refresh
Access tokens expire after 1 hour. No separate refresh flow - just request a new token.
User Authorization (Authorization Code Flow)
For apps that act on behalf of users.
Step 1: Redirect User to Authorize
https://zoom.us/oauth/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}
Use https://zoom.us/oauth/authorize for consent, but https://zoom.us/oauth/token for token exchange.
Optional Parameters:
| Parameter | Description |
|---|---|
state |
CSRF protection, maintains state through flow |
code_challenge |
For PKCE (see below) |
code_challenge_method |
S256 or plain (default: plain) |
Step 2: User Authorizes
- User signs in and grants permission
- Redirects to
redirect_uriwith authorization code:https://example.com/?code={AUTHORIZATION_CODE}
Step 3: Exchange Code for Token
POST https://zoom.us/oauth/token?grant_type=authorization_code&code={CODE}&redirect_uri={REDIRECT_URI}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
With PKCE: Add code_verifier parameter.
Response
{
"access_token": "eyJ...",
"token_type": "bearer",
"refresh_token": "eyJ...",
"expires_in": 3600,
"scope": "user:read:user",
"api_url": "https://api.zoom.us"
}
Refresh Token
POST https://zoom.us/oauth/token?grant_type=refresh_token&refresh_token={REFRESH_TOKEN}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
- Access tokens expire after 1 hour
- Refresh token lifetime can vary; ~90 days is common for some user-based flows. Treat it as configuration/behavior that can change and rely on runtime errors + re-auth fallback.
- Always use the latest refresh token for the next request
- If refresh token expires, redirect user to authorization URL to restart flow
User-Level vs Account-Level Apps
| Type | Who Can Authorize | Scope Access |
|---|---|---|
| User-level | Any individual user | Scoped to themselves |
| Account-level | User with admin permissions | Account-wide access (admin scopes) |
Device Authorization (Device Flow)
For devices without browsers (e.g., Meeting SDK apps).
Prerequisites
Enable "Use App on Device" in: Features > Embed > Enable Meeting SDK
Step 1: Request Device Code
POST https://zoom.us/oauth/devicecode?client_id={CLIENT_ID}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
Response
{
"device_code": "DEVICE_CODE",
"user_code": "abcd1234",
"verification_uri": "https://zoom.us/oauth_device",
"verification_uri_complete": "https://zoom.us/oauth/device/complete/{CODE}",
"expires_in": 900,
"interval": 5
}
Step 2: User Authorization
Direct user to:
verification_uriand displayuser_codefor manual entry, ORverification_uri_complete(user code prefilled)
User signs in and allows the app.
Step 3: Poll for Token
Poll at the interval (5 seconds) until user authorizes:
POST https://zoom.us/oauth/token?grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code={DEVICE_CODE}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
Response
{
"access_token": "eyJ...",
"token_type": "bearer",
"refresh_token": "eyJ...",
"expires_in": 3599,
"scope": "user:read:user user:read:token",
"api_url": "https://api.zoom.us"
}
Polling Responses
| Response | Meaning | Action |
|---|---|---|
| Token returned | User authorized | Store tokens, done |
error: authorization_pending |
User hasn't authorized yet | Keep polling at interval |
error: slow_down |
Polling too fast | Increase interval by 5 seconds |
error: expired_token |
Device code expired (15 min) | Restart flow from Step 1 |
error: access_denied |
User denied authorization | Handle denial, don't retry |
Polling Implementation
async function pollForToken(deviceCode, interval) {
while (true) {
await sleep(interval * 1000);
try {
const response = await axios.post(
`https://zoom.us/oauth/token?grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=${deviceCode}`,
null,
{ headers: { 'Authorization': `Basic ${credentials}` } }
);
return response.data; // Success - got tokens
} catch (error) {
const err = error.response?.data?.error;
if (err === 'authorization_pending') continue;
if (err === 'slow_down') { interval += 5; continue; }
throw error; // expired_token or access_denied
}
}
}
Refresh
Same as User Authorization. If refresh token expires, restart device flow from Step 1.
Client Authorization (Chatbot)
For chatbot message operations only.
Request Token
POST https://zoom.us/oauth/token?grant_type=client_credentials
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
Response
{
"access_token": "eyJ...",
"token_type": "bearer",
"expires_in": 3600,
"scope": "imchat:bot",
"api_url": "https://api.zoom.us"
}
Refresh
Tokens expire after 1 hour. No refresh flow - just request a new token.
Using Access Tokens
Call API
GET https://api.zoom.us/v2/users/me
Headers:
Authorization: Bearer {ACCESS_TOKEN}
Me Context
Replace userID with me to target the token's associated user:
| Endpoint | Methods |
|---|---|
/v2/users/me |
GET, PATCH |
/v2/users/me/token |
GET |
/v2/users/me/meetings |
GET, POST |
Revoke Access Token
Works for all authorization types.
POST https://zoom.us/oauth/revoke?token={ACCESS_TOKEN}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
Response
{
"status": "success"
}
PKCE (Proof Key for Code Exchange)
For public clients that can't securely store secrets (mobile apps, SPAs, desktop apps).
When to Use PKCE
| Client Type | Use PKCE? | Why |
|---|---|---|
| Mobile app | Yes | Can't securely store client secret |
| Single Page App (SPA) | Yes | JavaScript is visible to users |
| Desktop app | Yes | Binary can be decompiled |
| Meeting SDK (client-side) | Yes | Runs on user's device |
| Backend server | Optional | Can keep secrets, but PKCE adds security |
How PKCE Works
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Zoom │ │ Zoom │
│ App │ │ Auth │ │ Token │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ 1. Generate code_verifier (random) │ │
│ 2. Create code_challenge = SHA256(verifier) │
│ │ │
│ ─────── /authorize + code_challenge ──► │ │
│ │ │
│ ◄────── authorization_code ──────────── │ │
│ │ │
│ ─────────────── /token + code_verifier ─┼────────────────────────────► │
│ │ │
│ │ Verify: SHA256(verifier) │
│ │ == challenge │
│ │ │
│ ◄───────────────────────────────────────┼─────── access_token ──────── │
│ │ │
Implementation (Node.js)
const crypto = require('crypto');
function generatePKCE() {
const verifier = crypto.randomBytes(32).toString('base64url');
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
return { verifier, challenge };
}
const pkce = generatePKCE();
const authUrl = `https://zoom.us/oauth/authorize?` +
`response_type=code&` +
`client_id=${CLIENT_ID}&` +
`redirect_uri=${REDIRECT_URI}&` +
`code_challenge=${pkce.challenge}&` +
`code_challenge_method=S256`;
// Store pkce.verifier in session for callback
Token Exchange with PKCE
POST https://zoom.us/oauth/token?grant_type=authorization_code&code={CODE}&redirect_uri={REDIRECT_URI}&code_verifier={VERIFIER}
Headers:
Authorization: Basic {Base64(ClientID:ClientSecret)}
Deauthorization
When a user removes your app, Zoom sends a webhook to your Deauthorization Notification Endpoint URL.
Webhook Event
{
"event": "app_deauthorized",
"event_ts": 1740439732278,
"payload": {
"account_id": "ACCOUNT_ID",
"user_id": "USER_ID",
"signature": "SIGNATURE",
"deauthorization_time": "2019-06-17T13:52:28.632Z",
"client_id": "CLIENT_ID"
}
}
Requirements
- Delete all associated user data after receiving this event
- Verify webhook signature (use secret token, verification token deprecated Oct 2023)
- Only public apps receive deauthorization webhooks (not private/dev apps)
Pre-Approval Flow
Some Zoom accounts require Marketplace admin pre-approval before users can authorize apps.
- Users can request pre-approval from their admin
- Account-level apps (admin scopes) require appropriate role permissions
Active Apps Notifier (AAN)
In-meeting feature showing apps with real-time access to content.
- Displays icon + tooltip with app info, content type being accessed, approving account
- Supported: Zoom client 5.6.7+, Meeting SDK 5.9.0+
OAuth Scopes
Scope Types
| Type | Description | For |
|---|---|---|
| Classic scopes | Legacy scopes (user, admin, master levels) | Existing apps |
| Granular scopes | New fine-grained scopes with optional support | New apps |
Classic Scopes
For previously-created apps. Three levels:
- User-level: Access to individual user's data
- Admin-level: Account-wide access, requires admin role
- Master-level: For master-sub account setups, requires account owner
Full list: https://developers.zoom.us/docs/integrations/oauth-scopes/
Granular Scopes
For new apps. Format: <service>:<action>:<data_claim>:<access>
| Component | Values |
|---|---|
| service | meeting, webinar, user, recording, etc. |
| action | read, write, update, delete |
| data_claim | Data category (e.g., participants, settings) |
| access | empty (user), admin, master |
Example: meeting:read:list_meetings:admin
Full list: https://developers.zoom.us/docs/integrations/oauth-scopes-granular/
Optional Scopes
Granular scopes can be marked as optional - users choose whether to grant them.
Basic authorization (uses build flow defaults):
https://zoom.us/oauth/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}
Advanced authorization (custom scopes per request):
https://zoom.us/oauth/authorize?client_id={CLIENT_ID}&response_type=code&redirect_uri={REDIRECT_URI}&scope={required_scopes}&optional_scope={optional_scopes}
Include previously granted scopes:
https://zoom.us/oauth/authorize?...&include_granted_scopes&scope={additional_scopes}
Migrating Classic to Granular
- Manage > select app > edit
- Scope page > Development tab > click Migrate
- Review auto-assigned granular scopes, remove unnecessary, mark optional
- Test
- Production tab > click Migrate
Notes:
- No review needed if only migrating or reducing scopes
- Existing user tokens continue with classic scope values until re-authorization
- New users get granular scopes after migration
Common Error Codes
| 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, urn:ietf:params:oauth:grant-type:device_code, or client_credentials |
| 4706 | Client ID or secret missing | Add credentials to header or request params |
| 4709 | Redirect URI mismatch | Ensure redirect_uri matches app configuration exactly (including trailing slash) |
| 4711 | Refresh token invalid | Token scopes don't match client scopes |
| 4717 | App has been disabled | Contact Zoom support |
| 4733 | Code is expired | Authorization codes expire in 5 minutes - restart flow |
| 4734 | Invalid authorization code | Regenerate authorization code |
| 4735 | Owner of token does not exist | User was removed from account - re-authorize |
| 4741 | Token has been revoked | Use the most recent token from latest authorization |
See references/oauth-errors.md for complete error list.
Quick Reference
| Flow | Grant Type | Token Expiry | Refresh |
|---|---|---|---|
| Account (S2S) | account_credentials |
1 hour | Request new token |
| User | authorization_code |
1 hour | Use refresh_token (90 day expiry) |
| Device | urn:ietf:params:oauth:grant-type:device_code |
1 hour | Use refresh_token (90 day expiry) |
| Client (Chatbot) | client_credentials |
1 hour | Request new token |
Demo Guidance
If you build an OAuth demo app, document its runtime base URL in that demo project's own
README or .env.example, not in this shared skill.
Resources
- OAuth docs: https://developers.zoom.us/docs/integrations/oauth/
- S2S OAuth docs: https://developers.zoom.us/docs/internal-apps/s2s-oauth/
- PKCE blog: https://developers.zoom.us/blog/pcke-oauth-with-postman-rest-api/
- Classic scopes: https://developers.zoom.us/docs/integrations/oauth-scopes/
- Granular scopes: https://developers.zoom.us/docs/integrations/oauth-scopes-granular/
Integrated Index
This section was migrated from SKILL.md.
Quick Start Path
If you're new to Zoom OAuth, follow this order:
-
Run preflight checks first → RUNBOOK.md
-
Choose your OAuth flow → concepts/oauth-flows.md
- 4 flows: S2S (backend), User (SaaS), Device (no browser), Chatbot
- Decision matrix: Which flow fits your use case?
-
Understand token lifecycle → concepts/token-lifecycle.md
- CRITICAL: How tokens expire, refresh, and revoke
- Common pitfalls: refresh token rotation
-
Implement your flow → Jump to examples:
- Backend automation → examples/s2s-oauth-redis.md
- SaaS app → examples/user-oauth-mysql.md
- Mobile/SPA → examples/pkce-implementation.md
- Device (TV/kiosk) → examples/device-flow.md
-
Fix redirect URI issues → troubleshooting/redirect-uri-issues.md
- Most common OAuth error: Redirect URI mismatch
-
Implement token refresh → examples/token-refresh.md
- Automatic middleware pattern
- Handle refresh token rotation
-
Troubleshoot errors → troubleshooting/common-errors.md
- Error code tables (4700-4741 range)
- Quick diagnostic workflow
Documentation Structure
oauth/
├── SKILL.md # Main skill overview
├── SKILL.md # This file - navigation guide
│
├── concepts/ # Core OAuth concepts
│ ├── oauth-flows.md # 4 flows: S2S, User, Device, Chatbot
│ ├── token-lifecycle.md # Expiration, refresh, revocation
│ ├── pkce.md # PKCE security for public clients
│ ├── scopes-architecture.md # Classic vs Granular scopes
│ └── state-parameter.md # CSRF protection with state
│
├── examples/ # Complete working code
│ ├── s2s-oauth-basic.md # S2S OAuth minimal example
│ ├── s2s-oauth-redis.md # S2S OAuth with Redis caching (production)
│ ├── user-oauth-basic.md # User OAuth minimal example
│ ├── user-oauth-mysql.md # User OAuth with MySQL + encryption (production)
│ ├── device-flow.md # Device authorization flow
│ ├── pkce-implementation.md # PKCE for SPAs/mobile apps
│ └── token-refresh.md # Auto-refresh middleware pattern
│
├── troubleshooting/ # Problem solving guides
│ ├── common-errors.md # Error codes 4700-4741
│ ├── redirect-uri-issues.md # Most common OAuth error
│ ├── token-issues.md # Expired, revoked, invalid tokens
│ └── scope-issues.md # Scope mismatch errors
│
└── references/ # Reference documentation
├── oauth-errors.md # Complete error code reference
├── classic-scopes.md # Classic scope reference
└── granular-scopes.md # Granular scope reference
By Use Case
I want to automate Zoom tasks on my own account
- OAuth Flows - S2S OAuth explained
- S2S OAuth Redis - Production pattern with Redis caching
- Token Lifecycle - 1hr token, no refresh
I want to build a SaaS app for other Zoom users
- OAuth Flows - User OAuth explained
- User OAuth MySQL - Production pattern with encryption
- Token Refresh - Automatic refresh middleware
- Redirect URI Issues - Fix most common error
I want to build a mobile or SPA app
- PKCE - Why PKCE is required for public clients
- PKCE Implementation - Complete code example
- State Parameter - CSRF protection
I want to build an app for devices without browsers (TV, kiosk)
- OAuth Flows - Device flow explained
- Device Flow Example - Complete polling implementation
- Common Errors - Device-specific errors
I'm building a Team Chat bot
- OAuth Flows - Chatbot flow explained
- S2S OAuth Basic - Similar pattern, different grant type
- Scopes Architecture - Chatbot-specific scopes
I'm getting redirect URI errors (4709)
- Redirect URI Issues - START HERE!
- Common Errors - Error details
- User OAuth Basic - See correct pattern
I'm getting token errors (4700-4741)
- Token Issues - Diagnostic workflow
- Token Lifecycle - Understand expiration
- Token Refresh - Implement auto-refresh
- Common Errors - Error code tables
I'm getting scope errors (4711)
- Scope Issues - Mismatch causes
- Scopes Architecture - Classic vs Granular
- Classic Scopes - Complete scope reference
- Granular Scopes - Granular scope reference
I need to refresh tokens
- Token Lifecycle - When to refresh
- Token Refresh - Middleware pattern
- Token Issues - Common mistakes
I want to understand the difference between Classic and Granular scopes
- Scopes Architecture - Complete comparison
- Classic Scopes -
resource:levelformat - Granular Scopes -
service:action:data_claim:accessformat
I need to secure my OAuth implementation
- PKCE - Public client security
- State Parameter - CSRF protection
- User OAuth MySQL - Token encryption at rest
I want to migrate from JWT app to S2S OAuth
- S2S OAuth Redis - Modern replacement
- Token Lifecycle - Different token behavior
Note: JWT App Type was deprecated in June 2023. Migrate to S2S OAuth for server-to-server automation.
Most Critical Documents
1. OAuth Flows (DECISION DOCUMENT)
Understand which of the 4 flows to use:
- S2S OAuth: Backend automation (your account)
- User OAuth: SaaS apps (users authorize you)
- Device Flow: Devices without browsers
- Chatbot: Team Chat bots only
2. Token Lifecycle (MOST COMMON ISSUE)
99% of OAuth issues stem from misunderstanding:
- Token expiration (1 hour for all flows)
- Refresh token rotation (must save new refresh token)
- Revocation behavior (invalidates all tokens)
3. Redirect URI Issues (MOST COMMON ERROR)
troubleshooting/redirect-uri-issues.md
Error 4709 ("Redirect URI mismatch") is the #1 OAuth error. Must match EXACTLY (including trailing slash, http vs https).
Key Learnings
Critical Discoveries:
-
Refresh Token Rotation
- Each refresh returns a NEW refresh token
- Old refresh token becomes invalid
- Failure to save new token causes 4735 errors
- See: Token Refresh
-
S2S OAuth Uses Redis, User OAuth Uses Database
- S2S: Single token for entire account → Redis (ephemeral)
- User: Per-user tokens → Database (persistent)
- See: S2S OAuth Redis vs User OAuth MySQL
-
Redirect URI Must Match EXACTLY
- Trailing slash matters:
/callback≠/callback/ - Protocol matters:
http://≠https:// - Port matters:
:3000≠:3001 - See: Redirect URI Issues
- Trailing slash matters:
-
PKCE Required for Public Clients
- Mobile apps CANNOT keep secrets
- SPAs CANNOT keep secrets
- PKCE prevents authorization code interception
- See: PKCE
-
State Parameter Prevents CSRF
- Generate random state before redirect
- Store in session
- Verify on callback
- See: State Parameter
-
Token Storage Must Be Encrypted
- NEVER store tokens in plain text
- Use AES-256 minimum
- See: User OAuth MySQL
-
JWT App Type is Deprecated (June 2023)
- No new JWT apps can be created
- Existing apps still work but will eventually be sunset
- Migrate to S2S OAuth or User OAuth
-
Scope Levels Determine Authorization Requirements
- No suffix (user-level): Any user can authorize
:admin: Requires admin role:master: Requires account owner (multi-account)- See: Scopes Architecture
-
Authorization Codes Expire in 5 Minutes
- Exchange code for token immediately
- Don't cache authorization codes
- See: Token Lifecycle
-
Device Flow Requires Polling
- Poll at interval returned by
/devicecode(usually 5s) - Handle
authorization_pending,slow_down,expired_token - See: Device Flow
- Poll at interval returned by
Quick Reference
"Which OAuth flow should I use?"
"Redirect URI mismatch error (4709)"
"Token expired or invalid"
"Refresh token invalid (4735)"
→ Token Refresh - Must save new refresh token
"Scope mismatch error (4711)"
"How do I secure my OAuth app?"
→ PKCE + State Parameter
"How do I implement auto-refresh?"
"What's the difference between Classic and Granular scopes?"
"What error code means what?"
Document Version
Based on Zoom OAuth API v2 (2024+)
Deprecated: JWT App Type (June 2023)
Happy coding!
Remember: Start with OAuth Flows to understand which flow fits your use case!
Environment Variables
- See references/environment-variables.md for standardized
.envkeys and where to find each value.
04 同仓库更多 Skills
ticket-deflector
Reads a forwarded customer email or ticket, pulls order and refund status from a payments connector (PayPal, Square, or Stripe) or Shopify, account history from the CRM, and open tickets from a support desk (Zoho Desk), drafts a tone-matched reply in the owner's writing voice, and can issue a refund through the payments connector with explicit owner approval. With Shopify connected it also runs a proactive order-triage mode that surfaces orders needing attention — unfulfilled past the promised window, payment problems, pending refunds, stuck shipments — and drafts the next action for each before the customer has to ask. Use when the user says "draft a response," "answer this customer," "where's my order," "I want a refund," "check my orders," or "anything about to blow up."
tax-season-organizer
Prepares tax-season materials for the owner's accountant, not tax advice. US federal tax; a non-US business gets its closed-books packet instead. Two modes: (1) quarterly estimated tax from YTD net income in the ledger (MYOB, NetSuite, QuickBooks, Xero, or Zoho Books); (2) year-end 1099 prep, scanning the ledger, PayPal, and Stripe for contractors paid over USD 600 into a 1099-NEC list with missing W-9 flags. Any tax request routes first to /tax-prep, which confirms the books are closed and reconciled before running this skill. Use this skill directly only when the owner says the period's books are already closed: "books are closed, now do the 1099s," "run the quarterly estimate off the closed numbers," or "just the contractor W-9 list."
tax-prep
Prepares tax materials as a two-link chain — month-end-prep confirms the books are closed and reconciled first, then tax-season-organizer calculates the quarterly estimated payment or builds the year-end 1099-NEC list and accountant packet from those closed numbers. Requires a ledger (MYOB, NetSuite, QuickBooks, Xero, or Zoho Books); uses Gusto, PayPal, and Stripe when connected, else CSV upload. US federal tax math; a non-US business gets the closed-books packet. Prep material for a CPA, never tax advice, nothing filed. Trigger on "quarterly taxes," "estimated tax payment," "how much should I set aside," "1099s," "1099-NEC," "W-9s," "year-end tax prep," "get my books ready for my accountant," any phrasing that suggests a tax deadline is coming, or a question about net profit or YTD income that sounds like worry about a tax bill. An owner who says the period's books are already closed routes to tax-season-organizer directly.
speed-to-lead
Makes sure no inbound lead goes unanswered. Watches web forms, the shared inbox, and the CRM for new inquiries, qualifies each one against the owner's criteria, drafts a reply in the owner's voice with real meeting times for the owner to approve and send, routes anything hot to a human right away, and logs the whole thing to the CRM. Never sends on its own. Built to run on a schedule so overnight inquiries are drafted and sorted by morning, and works from forwarded or pasted inquiries when no connector is available. Use this whenever the owner is worried about inbound leads or response time — including phrasings like "leads are going cold," "did anyone answer that inquiry," "I keep missing form submissions," "nothing falls through the cracks," "handle my inbound," "respond to this lead," or "we lose people because nobody gets back fast enough." Reach for it for a single unanswered inquiry as readily as for a standing setup.
social-content-engine
Runs the owner's content operation as a standing thing rather than a one-off: keeps a rolling posting calendar, learns the owner's voice, generates on-brand Canva graphics, writes captions that sound like them, repurposes one asset into every channel format, and stages posts for approval instead of publishing on its own. Pulls product photos and prices from Shopify when connected; with no connectors, drafts the calendar and asset briefs for manual posting. Use this whenever the owner talks about posting, content, or social — including "I need to post more," "we've gone quiet on Instagram," "what should we post this month," "make the content," "give me a month of posts," "write this in my voice," "repurpose this," or "set up our content calendar." Reach for it for a single post as readily as for a full quarter. One handoff: a finished, approved campaign brief goes to canva-creator; this skill owns the standing operation.
smb-router
The front door to the Small Business plugin. Listens to what the owner needs right now — vague or specific — and routes them to the best skill or slash command for the moment. Also serves as a guide: explains what's available, suggests what to try next, and adapts recommendations based on stored business context. Trigger whenever the owner asks "what can you do," "help me with my business," "what should I focus on," "I don't know where to start," or any open-ended business request that doesn't clearly match a single skill.