Zoom
Communication Tool
Zoom
Overview
Zoom is a communication platform specializing in video conferencing. It supports up to 1,000 participants and provides screen sharing, recording functionality, and webinar features. With high-quality audio and video quality, demand continues even after the pandemic due to the establishment of remote work practices.
Details
Zoom is a video conferencing service established in 2011, now serving as a synonymous presence for video conferencing worldwide. With the establishment of remote work after the pandemic, demand continues to grow, expanding adoption in education, healthcare, and enterprise sectors.
Zoom's key strength is its simple, user-friendly interface and stable, high-quality video and audio communication. Using Zoom SDK and Zoom API, you can integrate Zoom functionality into your own applications or build custom solutions. It provides rich developer features including event notifications via Webhooks, extension development through Apps for Zoom, and phone integration via Zoom Phone API.
In 2024, the developer ecosystem was further enhanced with AI feature improvements, Zoom Apps Marketplace expansion, Zoom Contact Center API, and new SDK features. Particularly, Meeting SDK, Video SDK, and Cloud Recording API make it easy to embed Zoom functionality into other applications.
Pros and Cons
Pros
- High-Quality Communication: Stable audio and video quality
- Large-Scale Support: Supports up to 1,000 participants
- User-Friendly: Intuitive and easy-to-understand user interface
- Rich APIs: Comprehensive SDK, REST API, and Webhook offerings
- Recording & Broadcasting: Cloud recording and live streaming features
- Integration Features: Connectivity with calendars, CRM, LMS, etc.
- Security: End-to-end encryption and waiting room features
- Developer Ecosystem: Apps for Zoom and Marketplace
Cons
- Cost: High-feature plans are relatively expensive
- Security Concerns: Past security issues (now improved)
- Resource Consumption: CPU and bandwidth usage for high-quality communication
- API Limitations: Rate limits and usage restrictions
- Dependency: High dependency on Zoom services
- UI Changes: Need to adapt to frequent UI updates
Key Links
- Zoom Official Site
- Zoom Developer Platform
- Zoom API Documentation
- Zoom SDK Documentation
- Zoom Webhooks
- Apps for Zoom
- Zoom Marketplace
Code Examples
Basic Zoom Meeting API Usage
const axios = require('axios');
class ZoomAPI {
constructor(jwt) {
this.jwt = jwt;
this.baseURL = 'https://api.zoom.us/v2';
}
// Create meeting
async createMeeting(userId, meetingOptions) {
try {
const response = await axios.post(
`${this.baseURL}/users/${userId}/meetings`,
{
topic: meetingOptions.topic,
type: 2, // Scheduled meeting
start_time: meetingOptions.start_time,
duration: meetingOptions.duration,
settings: {
host_video: true,
participant_video: true,
waiting_room: true,
mute_upon_entry: true,
approval_type: 0 // Auto-approve
}
},
{
headers: {
'Authorization': `Bearer ${this.jwt}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
} catch (error) {
console.error('Error creating meeting:', error.response.data);
throw error;
}
}
// List meetings
async listMeetings(userId) {
try {
const response = await axios.get(
`${this.baseURL}/users/${userId}/meetings`,
{
headers: {
'Authorization': `Bearer ${this.jwt}`
}
}
);
return response.data;
} catch (error) {
console.error('Error fetching meetings:', error.response.data);
throw error;
}
}
// Get meeting details
async getMeeting(meetingId) {
try {
const response = await axios.get(
`${this.baseURL}/meetings/${meetingId}`,
{
headers: {
'Authorization': `Bearer ${this.jwt}`
}
}
);
return response.data;
} catch (error) {
console.error('Error fetching meeting details:', error.response.data);
throw error;
}
}
// Delete meeting
async deleteMeeting(meetingId) {
try {
await axios.delete(
`${this.baseURL}/meetings/${meetingId}`,
{
headers: {
'Authorization': `Bearer ${this.jwt}`
}
}
);
return { success: true };
} catch (error) {
console.error('Error deleting meeting:', error.response.data);
throw error;
}
}
}
// Usage example
const zoomAPI = new ZoomAPI(process.env.ZOOM_JWT);
async function scheduleMeeting() {
try {
const meeting = await zoomAPI.createMeeting('[email protected]', {
topic: 'Weekly Team Meeting',
start_time: '2024-01-15T10:00:00Z',
duration: 60
});
console.log('Meeting created:', meeting.join_url);
return meeting;
} catch (error) {
console.error('Failed to create meeting:', error);
}
}
Zoom SDK Integration (Meeting SDK)
<!DOCTYPE html>
<html>
<head>
<title>Zoom Meeting SDK Integration</title>
<script src="https://source.zoom.us/2.16.0/lib/vendor/react.min.js"></script>
<script src="https://source.zoom.us/2.16.0/lib/vendor/react-dom.min.js"></script>
<script src="https://source.zoom.us/2.16.0/lib/vendor/redux.min.js"></script>
<script src="https://source.zoom.us/2.16.0/lib/vendor/redux-thunk.min.js"></script>
<script src="https://source.zoom.us/2.16.0/lib/vendor/lodash.min.js"></script>
<script src="https://source.zoom.us/zoom-meeting-2.16.0.min.js"></script>
</head>
<body>
<div id="meetingSDKElement"></div>
<script>
var authEndpoint = 'https://your-server.com/api/zoom/signature';
var sdkKey = 'your-sdk-key';
var meetingNumber = 123456789;
var passWord = 'meeting-password';
var role = 0; // 0: Participant, 1: Host
var userName = 'User Name';
var userEmail = '[email protected]';
var registrantToken = '';
var zakToken = '';
document.getElementById('meetingSDKElement').style.display = 'block';
ZoomMtg.setZoomJSLib('https://source.zoom.us/2.16.0/lib', '/av');
ZoomMtg.preLoadWasm();
ZoomMtg.prepareWebSDK();
// Get JWT signature from remote
function getSignature() {
fetch(authEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
meetingNumber: meetingNumber,
role: role
})
}).then((response) => {
return response.json();
}).then((data) => {
console.log(data);
startMeeting(data.signature);
}).catch((error) => {
console.error(error);
});
}
function startMeeting(signature) {
ZoomMtg.init({
leaveUrl: 'https://your-website.com',
success: (success) => {
console.log(success);
ZoomMtg.join({
signature: signature,
meetingNumber: meetingNumber,
userName: userName,
sdkKey: sdkKey,
userEmail: userEmail,
passWord: passWord,
tk: registrantToken,
zak: zakToken,
success: (success) => {
console.log('Join meeting success');
},
error: (error) => {
console.log(error);
}
});
},
error: (error) => {
console.log(error);
}
});
}
getSignature();
</script>
</body>
</html>
Webhook Processing
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook signature verification
function verifyWebhook(payload, headers) {
const message = `v0:${headers['x-zm-request-timestamp']}:${payload}`;
const hashForVerify = crypto.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET)
.update(message)
.digest('hex');
const signature = `v0=${hashForVerify}`;
return headers['x-zm-signature'] === signature;
}
// Webhook endpoint
app.post('/zoom/webhook', (req, res) => {
const payload = JSON.stringify(req.body);
// Signature verification
if (!verifyWebhook(payload, req.headers)) {
return res.status(401).send('Unauthorized');
}
const { event, payload: eventPayload } = req.body;
switch (event) {
case 'meeting.started':
console.log('Meeting started:', eventPayload.object);
handleMeetingStarted(eventPayload.object);
break;
case 'meeting.ended':
console.log('Meeting ended:', eventPayload.object);
handleMeetingEnded(eventPayload.object);
break;
case 'meeting.participant_joined':
console.log('Participant joined:', eventPayload.object);
handleParticipantJoined(eventPayload.object);
break;
case 'meeting.participant_left':
console.log('Participant left:', eventPayload.object);
handleParticipantLeft(eventPayload.object);
break;
case 'recording.completed':
console.log('Recording completed:', eventPayload.object);
handleRecordingCompleted(eventPayload.object);
break;
default:
console.log('Unhandled event:', event);
}
res.status(200).send('OK');
});
// Event handlers
async function handleMeetingStarted(meeting) {
// Meeting start processing
console.log(`Meeting "${meeting.topic}" started at ${meeting.start_time}`);
// Slack notification example
await sendSlackNotification({
text: `🚀 Meeting "${meeting.topic}" has started!`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Meeting Started*\n*Topic:* ${meeting.topic}\n*ID:* ${meeting.id}\n*Host:* ${meeting.host_email}`
}
}
]
});
}
async function handleMeetingEnded(meeting) {
// Meeting end processing
console.log(`Meeting "${meeting.topic}" ended at ${meeting.end_time}`);
const duration = new Date(meeting.end_time) - new Date(meeting.start_time);
const minutes = Math.floor(duration / 60000);
await sendSlackNotification({
text: `✅ Meeting "${meeting.topic}" has ended (${minutes} minutes)`,
});
}
async function handleRecordingCompleted(recording) {
// Recording completion processing
console.log('Recording files:', recording.recording_files);
for (const file of recording.recording_files) {
if (file.file_type === 'MP4') {
console.log(`Video recording available: ${file.download_url}`);
// Download and upload recording file
await processRecording(file);
}
}
}
async function processRecording(recordingFile) {
// Recording file processing
const response = await fetch(recordingFile.download_url, {
headers: {
'Authorization': `Bearer ${process.env.ZOOM_JWT}`
}
});
if (response.ok) {
// Upload file to external storage (S3, etc.)
console.log('Recording processed successfully');
}
}
app.listen(3000, () => {
console.log('Zoom webhook server running on port 3000');
});
Apps for Zoom Development
// Apps for Zoom application example
const express = require('express');
const app = express();
app.use(express.static('public'));
app.use(express.json());
// Zoom Apps authentication
app.get('/authorize', (req, res) => {
const { code, state } = req.query;
if (code) {
// Get access token
exchangeCodeForToken(code)
.then(tokenData => {
// Save token
res.redirect('/app.html');
})
.catch(error => {
console.error('Authorization error:', error);
res.status(500).send('Authorization failed');
});
} else {
res.status(400).send('Authorization code not provided');
}
});
// Zoom Apps API endpoint
app.post('/api/meeting-info', async (req, res) => {
try {
// Get current meeting information
const meetingContext = await zoomSdk.getMeetingContext();
res.json({
meetingId: meetingContext.meetingId,
participants: meetingContext.participants,
duration: meetingContext.duration
});
} catch (error) {
console.error('Error getting meeting info:', error);
res.status(500).json({ error: 'Failed to get meeting info' });
}
});
// Zoom Apps client-side (public/app.html)
const zoomAppScript = `
<script src="https://appssdk.zoom.us/sdk.min.js"></script>
<script>
// Zoom Apps SDK initialization
zoomSdk.config({
capabilities: ['getRunningContext', 'getMeetingContext'],
version: '0.16.0'
}).then(() => {
console.log('Zoom Apps SDK initialized');
loadMeetingData();
}).catch((error) => {
console.error('SDK initialization failed', error);
});
async function loadMeetingData() {
try {
// Get running context
const runningContext = await zoomSdk.getRunningContext();
console.log('Running context:', runningContext);
// Get meeting context
const meetingContext = await zoomSdk.getMeetingContext();
console.log('Meeting context:', meetingContext);
// Update UI
document.getElementById('meetingId').textContent = meetingContext.meetingId;
document.getElementById('participants').textContent = meetingContext.participants?.length || 0;
} catch (error) {
console.error('Error loading meeting data:', error);
}
}
// Update participant list
async function updateParticipants() {
try {
const participants = await zoomSdk.getMeetingParticipants();
const participantsList = document.getElementById('participantsList');
participantsList.innerHTML = participants
.map(p => \`<li>\${p.displayName} - \${p.role}</li>\`)
.join('');
} catch (error) {
console.error('Error updating participants:', error);
}
}
// Set event listeners
zoomSdk.addEventListener('onParticipantChange', (event) => {
console.log('Participant change:', event);
updateParticipants();
});
zoomSdk.addEventListener('onMeetingConfigChanged', (event) => {
console.log('Meeting config changed:', event);
});
</script>
`;
Cloud Recording API
class ZoomRecordingAPI {
constructor(jwt) {
this.jwt = jwt;
this.baseURL = 'https://api.zoom.us/v2';
}
// Get recordings list
async getRecordings(userId, from, to) {
try {
const response = await axios.get(
`${this.baseURL}/users/${userId}/recordings`,
{
params: {
from,
to,
page_size: 300
},
headers: {
'Authorization': `Bearer ${this.jwt}`
}
}
);
return response.data;
} catch (error) {
console.error('Error fetching recordings:', error.response.data);
throw error;
}
}
// Get recording details
async getRecording(meetingId) {
try {
const response = await axios.get(
`${this.baseURL}/meetings/${meetingId}/recordings`,
{
headers: {
'Authorization': `Bearer ${this.jwt}`
}
}
);
return response.data;
} catch (error) {
console.error('Error fetching recording details:', error.response.data);
throw error;
}
}
// Download recording
async downloadRecording(downloadUrl, outputPath) {
try {
const response = await axios({
method: 'GET',
url: downloadUrl,
responseType: 'stream',
headers: {
'Authorization': `Bearer ${this.jwt}`
}
});
const writer = fs.createWriteStream(outputPath);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
} catch (error) {
console.error('Error downloading recording:', error);
throw error;
}
}
// Delete recording
async deleteRecording(meetingId) {
try {
await axios.delete(
`${this.baseURL}/meetings/${meetingId}/recordings`,
{
headers: {
'Authorization': `Bearer ${this.jwt}`
}
}
);
return { success: true };
} catch (error) {
console.error('Error deleting recording:', error.response.data);
throw error;
}
}
}
// Usage example
const recordingAPI = new ZoomRecordingAPI(process.env.ZOOM_JWT);
async function processRecentRecordings() {
const fromDate = '2024-01-01';
const toDate = '2024-01-31';
try {
const recordings = await recordingAPI.getRecordings('[email protected]', fromDate, toDate);
for (const meeting of recordings.meetings) {
console.log(`Processing recordings for meeting: ${meeting.topic}`);
for (const file of meeting.recording_files) {
if (file.file_type === 'MP4') {
const outputPath = `./recordings/${meeting.id}_${file.id}.mp4`;
await recordingAPI.downloadRecording(file.download_url, outputPath);
console.log(`Downloaded: ${outputPath}`);
}
}
}
} catch (error) {
console.error('Failed to process recordings:', error);
}
}
Environment Variables Configuration
# .env file
ZOOM_JWT=your-zoom-jwt-token
ZOOM_API_KEY=your-zoom-api-key
ZOOM_API_SECRET=your-zoom-api-secret
ZOOM_WEBHOOK_SECRET=your-webhook-secret-token
# SDK settings
ZOOM_SDK_KEY=your-sdk-key
ZOOM_SDK_SECRET=your-sdk-secret
# Zoom Apps settings
ZOOM_APP_CLIENT_ID=your-app-client-id
ZOOM_APP_CLIENT_SECRET=your-app-client-secret
ZOOM_APP_REDIRECT_URL=https://your-app.com/authorize
# External integrations
SLACK_WEBHOOK_URL=your-slack-webhook-url
AWS_S3_BUCKET=your-s3-bucket-name
Docker Configuration Example
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy application
COPY . .
# Environment variables
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Run application
CMD ["npm", "start"]