Zoom Connector

Connect Zoom to Brevo through Tajo to automatically sync meeting participants and webinar attendees as contacts, trigger post-meeting follow-up sequences, and track engagement metrics for your marketing automations.

Overview

PropertyValue
PlatformZoom
CategoryVideo Conferencing (Custom)
Setup ComplexityMedium
Official IntegrationNo
Data SyncedParticipants, Events, Webinars, Contacts
Auth MethodOAuth 2.0 / Server-to-Server OAuth

Features

  • Participant sync - Auto-create Brevo contacts from meeting participants
  • Webinar attendee capture - Sync webinar registrants and attendees
  • Meeting event triggers - Fire automations on meeting start, end, and recording events
  • Engagement tracking - Track attendance duration and participation metrics
  • Webinar follow-up - Trigger targeted email sequences based on webinar attendance
  • Recording notifications - Send recording links via Brevo email campaigns

Prerequisites

Before you begin, ensure you have:

  1. A Zoom account (Pro plan or above)
  2. A Zoom Server-to-Server OAuth app or OAuth app via Zoom App Marketplace
  3. A Brevo account with API access
  4. A Tajo account with connector permissions

Authentication

Terminal window
# Create a Server-to-Server OAuth app at marketplace.zoom.us
export ZOOM_ACCOUNT_ID=your_account_id
export ZOOM_CLIENT_ID=your_client_id
export ZOOM_CLIENT_SECRET=your_client_secret
// Get access token via Server-to-Server OAuth
const tokenResponse = await fetch('https://zoom.us/oauth/token', {
method: 'POST',
headers: {
'Authorization': `Basic ${Buffer.from(
`${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}`
).toString('base64')}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'account_credentials',
account_id: process.env.ZOOM_ACCOUNT_ID
})
});
const { access_token } = await tokenResponse.json();

OAuth 2.0 (User-Level)

// Authorization URL for user-level OAuth
const authUrl = 'https://zoom.us/oauth/authorize?' +
new URLSearchParams({
client_id: process.env.ZOOM_CLIENT_ID,
redirect_uri: 'https://your-app.com/callback',
response_type: 'code'
});
// Exchange code for tokens
const tokenResponse = await fetch('https://zoom.us/oauth/token', {
method: 'POST',
headers: {
'Authorization': `Basic ${Buffer.from(
`${process.env.ZOOM_CLIENT_ID}:${process.env.ZOOM_CLIENT_SECRET}`
).toString('base64')}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: 'https://your-app.com/callback'
})
});

Configuration

Basic Setup

connectors:
zoom:
enabled: true
account_id: "${ZOOM_ACCOUNT_ID}"
client_id: "${ZOOM_CLIENT_ID}"
client_secret: "${ZOOM_CLIENT_SECRET}"
sync:
participants: true
webinars: true
recordings: true
webhook:
secret_token: "${ZOOM_WEBHOOK_SECRET}"
verification_token: "${ZOOM_VERIFICATION_TOKEN}"
lists:
meeting_participants: 15
webinar_attendees: 16
webinar_registrants: 17

Field Mapping

field_mapping:
email: email
name: FIRSTNAME
join_time: MEETING_JOIN_DATE
duration: MEETING_DURATION
webinar_title: WEBINAR_NAME
attendance_status: ATTENDANCE_STATUS
registration_source: UTM_SOURCE

API Endpoints

EndpointMethodDescription
https://api.zoom.us/v2/usersGETList users
https://api.zoom.us/v2/users/{userId}/meetingsGETList meetings
https://api.zoom.us/v2/meetings/{meetingId}GETGet meeting details
https://api.zoom.us/v2/past_meetings/{meetingId}/participantsGETList past meeting participants
https://api.zoom.us/v2/users/{userId}/webinarsGETList webinars
https://api.zoom.us/v2/webinars/{webinarId}/registrantsGETList webinar registrants
https://api.zoom.us/v2/webinars/{webinarId}/participantsGETList webinar participants
https://api.zoom.us/v2/meetings/{meetingId}/recordingsGETGet meeting recordings
https://api.zoom.us/v2/webhooksPOSTSubscribe to webhooks

Code Examples

Initialize Connector

import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({
apiKey: process.env.TAJO_API_KEY,
brevoApiKey: process.env.BREVO_API_KEY
});
await tajo.connectors.connect('zoom', {
accountId: process.env.ZOOM_ACCOUNT_ID,
clientId: process.env.ZOOM_CLIENT_ID,
clientSecret: process.env.ZOOM_CLIENT_SECRET
});

Sync Meeting Participants

// Retrieve past meeting participants
const response = await fetch(
`https://api.zoom.us/v2/past_meetings/${meetingId}/participants`,
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
);
const { participants } = await response.json();
for (const participant of participants) {
if (participant.user_email) {
await tajo.contacts.sync({
email: participant.user_email,
attributes: {
FIRSTNAME: participant.name,
MEETING_DURATION: participant.duration,
MEETING_JOIN_DATE: participant.join_time,
ATTENDANCE_STATUS: 'attended'
},
listIds: [15]
});
}
}

Sync Webinar Attendees

// Get webinar attendees and sync to Brevo
const attendeesResponse = await fetch(
`https://api.zoom.us/v2/past_webinars/${webinarId}/participants`,
{
headers: { 'Authorization': `Bearer ${accessToken}` }
}
);
const { participants: attendees } = await attendeesResponse.json();
for (const attendee of attendees) {
await tajo.contacts.sync({
email: attendee.user_email,
attributes: {
FIRSTNAME: attendee.name,
WEBINAR_NAME: webinarTitle,
ATTENDANCE_STATUS: 'attended',
MEETING_DURATION: attendee.duration
},
listIds: [16]
});
}

Handle Zoom Webhooks

app.post('/webhooks/zoom', async (req, res) => {
// Handle Zoom URL validation challenge
if (req.body.event === 'endpoint.url_validation') {
const hashForValidation = crypto
.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET)
.update(req.body.payload.plainToken)
.digest('hex');
return res.json({
plainToken: req.body.payload.plainToken,
encryptedToken: hashForValidation
});
}
// Verify webhook signature
const message = `v0:${req.headers['x-zm-request-timestamp']}:${JSON.stringify(req.body)}`;
const hash = crypto
.createHmac('sha256', process.env.ZOOM_WEBHOOK_SECRET)
.update(message)
.digest('hex');
const signature = `v0=${hash}`;
if (req.headers['x-zm-signature'] !== signature) {
return res.status(401).send('Unauthorized');
}
const { event, payload } = req.body;
await tajo.connectors.handleWebhook('zoom', {
topic: event,
payload: payload
});
res.status(200).send('OK');
});

Rate Limits

CategoryLimitNotes
Light API calls30 req/secGET user, meeting info
Medium API calls20 req/secList participants, webinars
Heavy API calls10 req/secReports, recordings
Daily limit5,000+Depends on plan level

Rate Limit Headers

Zoom returns X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After headers. Implement backoff logic based on these headers to avoid 429 errors.

Troubleshooting

IssueCauseSolution
401 UnauthorizedToken expiredRefresh Server-to-Server OAuth token
Missing participantsMeeting not endedWait until meeting ends for full data
Webhook validation failsWrong secretVerify webhook secret in Zoom Marketplace
No email dataGuest participantsEnable registration to capture emails
Rate limit 429Too many requestsImplement exponential backoff

Debug Mode

connectors:
zoom:
debug: true
log_level: verbose
log_webhooks: true

Best Practices

  1. Use Server-to-Server OAuth - Simpler authentication without user interaction
  2. Enable webinar registration - Required to capture attendee email addresses
  3. Process after meeting end - Participant data is only complete after meetings end
  4. Segment by event type - Assign different Brevo lists for meetings vs. webinars
  5. Track engagement metrics - Use duration and join time for lead scoring
  6. Send recording follow-ups - Automate recording link delivery via Brevo

Security

  • OAuth 2.0 - Server-to-Server or user-level OAuth authentication
  • Webhook verification - HMAC-SHA256 signature validation
  • URL validation - Challenge-response verification for webhook endpoints
  • Scoped permissions - Request minimum required OAuth scopes
  • Token rotation - Server-to-Server tokens auto-expire (1 hour)
  • Encrypted transport - TLS 1.2+ for all API communications

Open-Source Implementation Map

This section is derived from official or public repository material discovered for the Zoom connector. Use it as the engineering companion to the setup guide above: it shows where the API surface lives, what implementation assets exist, and how Tajo should translate them into reliable Brevo sync behavior.

Repository Snapshot

RepositoryCommitLanguages / formatsFiles
zoom/api4429982erb (170), JavaScript (12), scss (5), Markdown (3), png (3), Ruby (2)208

Integration Shape

graph LR
Source["Zoom API / repository"] --> Auth["Auth and scopes"]
Source --> Objects["Objects, events, and schemas"]
Auth --> Tajo["Tajo connector runtime"]
Objects --> Tajo
Tajo --> Brevo["Brevo contacts, attributes, lists, campaigns"]
Tajo --> Ops["Backfill, cursor, retries, logs"]

What To Reuse

  • This definition is no longer being maintained. Please visit our new documentation to get started using the Zoom API.
  • Zoom API Version 2
  • This repo is the source code for our documentation. You can view the documentation here.
  • Feel free to submit issues or fork this repo and contribute changes.
  • Getting Started

Tajo Revamp Checklist

  • Keep authentication setup aligned with the vendor docs and the public repository’s current API shape.
  • Map primary resources into explicit Tajo sync objects with stable external IDs.
  • Prefer cursor-based or updated-at incremental sync where the API exposes it; otherwise document the fallback.
  • Treat webhook handlers as idempotent and replay-safe, especially for order, contact, ticket, and campaign events.
  • Capture pagination, rate limits, retry headers, and partial-failure behavior in connector smoke tests.
  • Keep examples small and runnable against sandbox or test-mode accounts.

Sources

Subscribe to updates

developer-docs

Drop your email or phone number — we'll send you what matters next.

auto-detect
AI Assistant

Hi! Ask me anything about the docs.