Zoom Apps SDK
OpenAIOfficialCreated by ZoomDistributed by OpenAI
- Skill
- Tool integration
- Software development
- MIT
- Medium risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The Zoom Apps SDK skill provides a structured approach for developers building applications that run directly inside the Zoom client. It covers the lifecycle of a Zoom App, from initial configuration and context verification to implementing advanced features like the Layers API, in-client OAuth, and inter-instance communication. The skill emphasizes the importance of identifying the correct running context—such as meetings, webinars, or the main client—to ensure features are gated appropriately and the user experience remains consistent across different Zoom environments.
What it does
- Initializes the Zoom Apps SDK with required capabilities.
- Identifies the current running context, such as meeting or main client.
- Facilitates in-client OAuth flows for secure user authentication.
- Enables communication between different app instances.
- Provides access to meeting-specific data and participant information.
- Supports advanced UI and media features like virtual backgrounds and notification management.
When to use it
- When building an application intended to run inside the Zoom client interface.
- When you need to access meeting context, participant lists, or recording status.
- When implementing custom UI overlays or media controls within a Zoom meeting.
- When you need to authenticate users without redirecting them to an external browser.
Advantages
- Provides a unified API for interacting with various Zoom client contexts.
- Supports secure, in-client authentication workflows.
- Enables deep integration with Zoom meeting features and participant data.
- Offers clear patterns for handling cross-instance communication.
Usage examples
- “How do I initialize the Zoom Apps SDK?”
- You must call zoomSdk.config() first, passing in the required capabilities and version to initialize the SDK and declare the features your app intends to use.
- “How can I get the current meeting ID?”
- After initializing the SDK, you can call zoomSdk.getMeetingContext() to retrieve the meetingID, meetingTopic, and meetingStatus.
- “How do I trigger authentication within the Zoom client?”
- Use the zoomSdk.authorize() method to trigger the in-client OAuth flow, which avoids external browser redirects and fires an onAuthorized event upon completion.
- “How do I send a message to another instance of my app?”
- First call zoomSdk.connect() to establish the connection, then use zoomSdk.postMessage() to send a payload to other app instances.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/openai/zoom-apps-sdk/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip zoom-apps-sdk.zip -d .agents/skills
Documentation by Zoom
Zoom Apps SDK
Use this skill when the app runs inside the Zoom client. If the user only needs to embed a meeting in an external app, route to Meeting SDK instead.
Workflow
Confirm the running context: meeting, webinar, main client, phone, collaborate mode, immersive mode, camera mode, or Layers API.
Configure Marketplace app settings, allowed domains, redirect URIs, and in-client OAuth before implementing SDK calls.
Initialize
zoomSdkand gate features by capability and running context.Design client communication and data flow: frontend SDK calls, backend REST calls, in-client OAuth tokens, and webhook handoff.
Implement advanced client features only after the base app loads reliably: Layers API, breakout rooms, guest mode, collaborate mode, or ZMail.
Debug blank panels, domain allowlist issues, CSP, missing capabilities, running-context mismatch, and OAuth redirect problems independently.
References
Full preserved guide: references/full-guide.md
Architecture: concepts/architecture.md
Running contexts: concepts/running-contexts.md
Meeting SDK versus Zoom Apps: concepts/meeting-sdk-vs-zoom-apps.md
Layers API: references/layers-api.md
In-client OAuth: examples/in-client-oauth.md
Common issues: troubleshooting/common-issues.md
Files
- ATTRIBUTION.mdAdded by AutoGineer873 B
- LICENSELicence file added by AutoGineer1.1 KiB
- RUNBOOK.md3.3 KiB
- SKILL.md1.5 KiB
- agents/openai.yaml130 B
- concepts/architecture.md7.7 KiB
- concepts/meeting-sdk-vs-zoom-apps.md1.0 KiB
- concepts/running-contexts.md5.6 KiB
- concepts/security.md5.4 KiB
- examples/app-communication.md3.6 KiB
- examples/breakout-rooms.md3.2 KiB
- examples/collaborate-mode.md4.1 KiB
- examples/guest-mode.md3.5 KiB
- examples/in-client-oauth.md6.1 KiB
- examples/layers-camera.md5.7 KiB
- examples/layers-immersive.md7.9 KiB
- examples/quick-start.md7.3 KiB
- references/apis.md5.3 KiB
- references/environment-variables.md1.1 KiB
- references/events.md3.8 KiB
- references/full-guide.md24.9 KiB
- references/layers-api.md15.8 KiB
- references/oauth.md5.3 KiB
- references/zmail-sdk.md5.5 KiB
- troubleshooting/common-issues.md4.3 KiB
- troubleshooting/debugging.md3.5 KiB
- troubleshooting/forum-top-questions.md1.1 KiB
- troubleshooting/migration.md3.0 KiB
examples/quick-start.md
Quick Start - Hello World Zoom App
Complete working Zoom App with Express backend and SDK frontend.
Prerequisites
Node.js 18+
ngrok account (free tier works)
Zoom Marketplace app (type: "Zoom App")
Project Setup
package.json
{
"name": "zoom-app-hello-world",
"version": "1.0.0",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.0",
"cookie-session": "^2.0.0",
"axios": "^1.6.0",
"dotenv": "^16.0.0"
}
}.env
ZOOM_APP_CLIENT_ID=your_client_id
ZOOM_APP_CLIENT_SECRET=your_client_secret
ZOOM_APP_REDIRECT_URI=https://xxxxx.ngrok.io/auth
SESSION_SECRET=generate_a_random_string_hereserver.js
require('dotenv').config();
const express = require('express');
const crypto = require('crypto');
const cookieSession = require('cookie-session');
const axios = require('axios');
const app = express();
app.use(express.json());
app.use(express.static('public'));
// Cookie session - SameSite=None required for Zoom embedded browser
app.use(cookieSession({
name: 'session',
keys: [process.env.SESSION_SECRET],
maxAge: 24 * 60 * 60 * 1000,
sameSite: 'none',
secure: true
}));
// OWASP security headers (required for Marketplace approval)
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000');
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('Content-Security-Policy', "frame-ancestors 'self' zoom.us *.zoom.us");
res.setHeader('Referrer-Policy', 'same-origin');
next();
});
// Step 1: Install - redirect to Zoom OAuth
app.get('/install', (req, res) => {
const verifier = crypto.randomBytes(32).toString('hex');
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
const state = crypto.randomBytes(16).toString('hex');
req.session.codeVerifier = verifier;
req.session.state = state;
const url = `https://zoom.us/oauth/authorize?` +
`client_id=${process.env.ZOOM_APP_CLIENT_ID}` +
`&response_type=code` +
`&redirect_uri=${encodeURIComponent(process.env.ZOOM_APP_REDIRECT_URI)}` +
`&code_challenge=${challenge}` +
`&code_challenge_method=S256` +
`&state=${state}`;
res.redirect(url);
});
// Step 2: OAuth callback - exchange code for tokens
app.get('/auth', async (req, res) => {
const { code, state } = req.query;
if (state !== req.session.state) {
return res.status(403).send('Invalid state');
}
try {
// Exchange authorization code for tokens
const tokenResponse = await axios.post('https://zoom.us/oauth/token', null, {
params: {
grant_type: 'authorization_code',
code,
redirect_uri: process.env.ZOOM_APP_REDIRECT_URI,
code_verifier: req.session.codeVerifier
},
headers: {
'Authorization': 'Basic ' + Buffer.from(
`${process.env.ZOOM_APP_CLIENT_ID}:${process.env.ZOOM_APP_CLIENT_SECRET}`
).toString('base64')
}
});
req.session.tokens = tokenResponse.data;
// Get deeplink to open app in Zoom client
const deeplink = await axios.post('https://api.zoom.us/v2/zoomapp/deeplink',
{ action: '' },
{ headers: { 'Authorization': `Bearer ${tokenResponse.data.access_token}` } }
);
res.redirect(deeplink.data.deeplink);
} catch (error) {
console.error('OAuth error:', error.response?.data || error.message);
res.status(500).send('Authorization failed');
}
});
// API endpoint for frontend
app.get('/api/user', (req, res) => {
if (!req.session.tokens) {
return res.status(401).json({ error: 'Not authenticated' });
}
res.json({ authenticated: true });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));public/index.html
<!DOCTYPE html>
<html>
<head>
<title>My Zoom App</title>
<script src="https://appssdk.zoom.us/sdk.js"></script>
<style>
body { font-family: -apple-system, sans-serif; padding: 20px; }
.info { background: #f0f4f8; padding: 16px; border-radius: 8px; margin: 12px 0; }
.error { background: #fff0f0; color: #c00; padding: 16px; border-radius: 8px; }
button { padding: 10px 20px; background: #2d8cff; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<h1>Hello Zoom App!</h1>
<div id="content">Loading...</div>
<script>
// CRITICAL: Do NOT use "let zoomSdk" - causes SyntaxError
let sdk = window.zoomSdk;
async function init() {
try {
const configResponse = await sdk.config({
capabilities: [
'shareApp',
'getMeetingContext',
'getUserContext',
'openUrl'
],
version: '0.16'
});
const context = configResponse.runningContext;
let html = `<div class="info"><strong>Running in:</strong> ${context}</div>`;
if (context === 'inMeeting' || context === 'inWebinar') {
const meeting = await sdk.getMeetingContext();
const user = await sdk.getUserContext();
html += `<div class="info"><strong>Meeting:</strong> ${meeting.meetingTopic || meeting.meetingID}</div>`;
html += `<div class="info"><strong>User:</strong> ${user.screenName} (${user.role})</div>`;
html += `<button onclick="shareApp()">Share App</button>`;
} else {
html += `<div class="info">Open this app during a meeting for full features.</div>`;
}
document.getElementById('content').innerHTML = html;
} catch (error) {
// Not running inside Zoom - show preview mode
document.getElementById('content').innerHTML =
'<div class="error">Not running inside Zoom client. ' +
'<a href="/install">Install App</a> to get started.</div>';
}
}
async function shareApp() {
try {
await sdk.shareApp();
} catch (e) {
console.error('Share failed:', e);
}
}
// Initialize with timeout fallback
document.addEventListener('DOMContentLoaded', () => {
init();
setTimeout(() => {
if (document.getElementById('content').textContent === 'Loading...') {
document.getElementById('content').innerHTML =
'<div class="error">SDK timed out. <a href="/install">Install App</a></div>';
}
}, 3000);
});
</script>
</body>
</html>Running Locally
# 1. Install dependencies
npm install
# 2. Start ngrok tunnel
ngrok http 3000
# 3. Copy the https URL (e.g., https://abc123.ngrok.io)
# 4. Update .env with ngrok URL
# ZOOM_APP_REDIRECT_URI=https://abc123.ngrok.io/auth
# 5. Start server
npm run devMarketplace Configuration
In Zoom Marketplace -> Your App:
App Credentials: Copy Client ID and Secret to
.envFeature tab -> Zoom App:
Home URL:
https://abc123.ngrok.ioRedirect URL:
https://abc123.ngrok.io/authDomain Allow List:
abc123.ngrok.io
Scopes tab: Add
zoomapp:inmeetingLocal Test: Click your app name in Zoom client sidebar to open
Next Steps
Add In-Client OAuth for seamless re-authorization
Add Layers API for immersive experiences
Review Security headers for Marketplace approval
Requirements and compatibility
Written for: OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
What our scanner noticed· Medium risk
- Possible data-exfiltration endpoints
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Zoom
- Publisher
- OpenAI (Official)
- Distributed by
- OpenAI
- Source
- openai/plugins · plugins/zoom/skills/zoom-apps-sdk · commit d420d13
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from plugins/zoom/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Zoom or OpenAI.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download zoom-apps-sdk.zip