Zoom
zoom.us ↗Works in your own Zoom account in about an hour - General app, redirect URL, allow list, code exchange - but installing for anyone outside your account means Marketplace review with an OWASP-facing security audit and scope-by-scope least-privilege justification.
01Pick your path
02Time & effort
- DEVCreate General app + credentials10–20 minutesinferred[S10][S5]Marketplace generates separate development and production client ID/secret.
- DEVRedirect URL, allow list, scopes15–30 minutessourced[S10][S7]OAuth redirect URL and OAuth allow lists are both required; scope_description is what reviewers read.
- DEVImplement authorize + token + refresh30–120 minutesinferred[S2]Standard RFC 6749 code flow; PKCE supported for public clients.
- PRODReview submission pack1–3 dayssourced[S9][S1]Technical Design section in the build flow, plus Terms of Use, Privacy Policy, docs and support URL.
- PRODMarketplace review cycleswait 2–6 weekssourced[S1][S9]Zoom states review time varies with app quality and scope clarity but gives no SLA.
03Steps
Get a working connection
Sign in to the Marketplace as owner/admin or with the 'Zoom for developers' role. The Developer link only renders when Zoom detects the permission - if it is missing, an admin must grant it first.
“The Marketplace only displays the Developer link when it detects that the user has the proper developer permissions.” — [S10] verified verbatim
↗ marketplace.zoom.us/◆ prepared by agent · Zoom App Marketplacehttps://marketplace.zoom.us/
Lower-left Developer > Develop > Build an app > General app > Create. This is the user-managed OAuth app type that acts on behalf of an authorized user. Server-to-Server is the wrong choice for per-user agents.
“On the app creation screen, select General app and click Create.” — [S10] verified verbatim
On Basic Info: set the OAuth redirect URL, add your base URL to the OAuth allow lists (required), enable Strict Mode so only exact matches are accepted, and keep development and production credentials separate. Custom URL schemes only work for Meeting SDK apps.
“OAuth allow lists (required): Add any unique URLs that Zoom should allow as valid redirects for your OAuth flows.” — [S10] verified verbatim
◆ prepared by agent · build-flow OAuth manifest (edit before pasting){ "oauth_information": { "usage": "USER_OPERATION", "development_redirect_uri": "https://yourapp.example.com/oauth/zoom/callback", "production_redirect_uri": "https://yourapp.example.com/oauth/zoom/callback", "oauth_allow_list": ["https://yourapp.example.com"], "strict_mode": true, "scopes": [ { "scope": "user:read:user", "optional": false }, { "scope": "user:read:email", "optional": false } ], "scope_description": "Identify the signed-in user (name + email) to display in the agent UI. No write or admin access.", "enable_public_pkce_client": true } }Pick the fewest scopes that ship v1. Write a scope_description for each; the review team verifies least privilege and may require unused scopes be removed. Do not take meeting/recording/admin scopes you cannot point to a screen for.
“The Marketplace Security Review Team verifies that the app only requests the minimum necessary OAuth scopes required for its functionality. Developers may be asked to remove unused or inappropriate scopes.” — [S9] verified verbatim
◆ prepared by agent · scope justification draft for reviewScope justification - user:read:user + user:read:email (v1) What the agent does: reads the signed-in user's name and email to label its session and route requests to the right person. Why these scopes: both are read-only and limited to the authorizing user's own profile. We request no meeting, recording, chat or phone scopes, and no ':admin' scopes. What we deliberately do NOT take: meeting create/update (not in v1), recordings (out of scope), admin-managed account access (we are a user-managed app). Data handling: tokens stored encrypted at rest; refresh tokens revoked on uninstall via Zoom's deauthorization callback; no data shared with third parties or used for advertising.
Send the user to https://zoom.us/oauth/authorize with response_type=code, client_id and redirect_uri; POST the returned code to https://zoom.us/oauth/token with a Basic auth header (base64 client_id:client_secret). Use PKCE for public clients with no secret. Verify the state parameter.
“https://zoom.us/oauth/authorize?response_type=code&client_id=ZOOM_CLIENT_ID&redirect_uri=REDIRECT_URI” — [S2] verified verbatim
↗ developers.zoom.us/docs/integrations/oauth/◆ prepared by agent · authorize + token exchangeconst authUrl = new URL('https://zoom.us/oauth/authorize'); authUrl.search = new URLSearchParams({ response_type: 'code', client_id: process.env.ZOOM_CLIENT_ID, redirect_uri: process.env.ZOOM_REDIRECT_URI, state, }).toString(); // in your callback handler const basic = Buffer.from(`${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}`).toString('base64'); const res = await fetch('https://zoom.us/oauth/token', { method: 'POST', headers: { Authorization: `Basic ${basic}` }, body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: process.env.ZOOM_REDIRECT_URI, }), }); const { access_token, refresh_token, expires_in } = await res.json(); // expires_in: 3600Tokens live 3600 seconds. Refresh before expiry, store refresh tokens encrypted, and implement Zoom's deauthorization endpoint so uninstalls invalidate data. Treat a scope/webhook change as a forced re-consent event for existing users.
“Access tokens expire after one hour. After your access token expires, you can follow the refresh flow to get a new one.” — [S2] verified verbatim
Use the Local Test page's 'Add App Now' to install into your own account and run a real consent + token exchange in a browser (curl cannot validate your client ID). Then choose: private app (no review), beta (no review, restricted sharing), or submit for public/unlisted review.
“You can get a user authorization code by adding your app to your Zoom account. To do this, navigate to the Local Test page, and choose Add App Now.” — [S2] verified verbatim
Make it production-ready
Complete the Technical Design section, attach Terms of Use / Privacy Policy / docs / support URL, and expect two phases: metadata + usability + compliance, then a security audit with OWASP Top 10 testing and scope evaluation. Rejections come back as a remediation report and re-submission.
“In the first phase of the submission process, the Marketplace Operations team reviews metadata, usability, and compliance.” — [S1] verified verbatim
↗ developers.zoom.us/docs/distribute/
04Requirements, scopes & nuances
You'll need
- ▸A Zoom account where you are owner, account admin, or hold the 'Zoom for developers' role (admin enables it under User Management > Roles > Role Settings > Advanced features). sourced ❝[S10]
- ▸A public HTTPS redirect endpoint, entered as the OAuth redirect URL and added to the OAuth allow list (both are required fields). sourced ❝[S10][S7]
- ▸For any public/unlisted listing: Terms of Use, Privacy Policy, self-serve documentation and a Support URL, plus ongoing individual user support. sourced ❝[S1]
- ▸A completed Technical Design section in the build flow describing stack, architecture and how each OAuth scope's data is used. sourced ❝[S9]
- ▸Willingness to undergo a security test against OWASP Top 10, including least-privilege verification, web app scanning and vulnerable-library checks. sourced ❝[S9][S1]
Gotchas
- !A token from a Free-plan account unlocks the same scopes as Business+, but your app shares account-level rate limits with every other app installed on that account - a big customer on Free throttles fast. inferred
- !Private and beta apps skip review entirely; public and unlisted apps do not. 'Unlisted' is not a loophole - it still goes through the full review. sourced ❝[S9][S1]
- !Access tokens expire after 3600 seconds. You must store and use the refresh_token, and you must handle Zoom's deauthorization callback so uninstalls are cleaned up. sourced ❝[S2]
- !Redirect handling is fussy: OAuth redirect URL is required, the OAuth allow list is required, and Strict Mode makes every redirect match exactly. Custom URL schemes are only allowed for Meeting SDK apps. sourced ❝[S10][S7]
- !Any scope or webhook change forces existing users through the consent screen again - decide your v1 scope set before launch, not after. sourced ❝[S2]
- !Server-to-Server OAuth (grant_type=account_credentials) is not user OAuth: it returns a token for your own account and has no refresh flow. Do not pick it if an agent must act per-user. sourced ❝[S2]
| Scope / permission | Tier | Note | Evidence |
|---|---|---|---|
| user:read:user | basic | Read the authorized user's profile. Appears in Zoom's own token response examples - a safe baseline. | sourced ❝[S2] |
| user:read:email | basic | Used in the official manifest schema example; good enough to identify a user in your agent's UI. | sourced ❝[S7] |
| openid email profile | basic | The only scopes advertised in Zoom's OIDC discovery document (probe). OIDC-style sign-in only; grants no Zoom API resources. | tested[probe] |
| user:read:token | sensitive | Returned in Zoom's device-flow example. Reading a user's token is higher risk - expect the security review to ask why email/profile is not enough. | sourced ❝[S2] |
| meeting read/write scopes (e.g. 'View and manage all user meetings') | sensitive | Needed for schedule/update/cancel automation; treat as sensitive and justify per-endpoint. Nothing in the fetched sources enumerates the exact scope strings. | inferred[S3][S4] |
| admin-level scopes (':admin' suffix, admin-managed apps) | gated | Account-wide access - only for admin-managed apps where an account admin installs. Requires admin role setup and is the most heavily scrutinised in review. | sourced ❝[S2][S3][S10] |
05If you get blocked
→ Ask the account admin: User Management > Roles > Role Settings > Advanced features > View+Edit on 'Zoom for developers'. The Developer link appears only once the permission exists.
→ Delete unused scopes before submission and fill scope_description for each; the security team explicitly may require removal of unused or inappropriate scopes.
→ Ship as a private app (users within your Zoom account) or a beta app first - both skip review - then submit for public listing when the product is stable.
06Evidence: what was tested, sourced, and still uncertain
Sources read
- [S1] App distribution - Distribute - Zoom Developer Docs official
- [S2] OAuth 2.0 - Integrations - Zoom Developer Docs official
- [S3] Internal apps (Server-to-server) - Internal Apps - Zoom Developer Docs official
- [S4] API Reference - API - Zoom Developer Docs official
- [S5] Integrations (OAuth apps) - Integrations - Zoom Developer Docs official
- [S6] Zoom Build Platform - Build - Zoom Developer Docs official
- [S7] OAuth information official
- [S8] Commerce - Rate Limits official
- [S9] App Review Process official
- [S10] Create an OAuth app official
- [S11] Rate limits - API - Zoom Developer Docs official
- [composio] Composio — Managed OAuth apps directory official
- [probe] Live HTTP probes run by the agent (dummy client_id, no credentials) probe
Still uncertain
- ? Zoom publishes no review SLA; the 2–6 week range is my estimate from the documented two-phase process, not a sourced number.
- ? The fetched sources do not enumerate the Zoom API scope catalogue or per-scope risk tiers; the meeting/recording/admin tier assignments are inferred from the API surface and the ':admin' suffix in a token example.
- ? The OIDC discovery document advertises only openid email profile, so it cannot be used to verify which API scopes are requestable - only the app build flow shows that.
- ? Whether Zoom issues an id_token with usable profile claims for OIDC-only clients is not covered by the sources fetched.
- ? No source states whether a production app can stay private indefinitely without review; private apps are documented as internal-account only.
- ? Generated automatically; no app was registered and no review was submitted.