Zoom 连接器
通过 Tajo 将 Zoom 连接到 Brevo,自动将会议参与者和网络研讨会出席者同步为联系人,触发会议后跟进序列,并追踪营销自动化的互动指标。
概览
| 属性 | 值 |
|---|---|
| 平台 | Zoom |
| 类别 | 视频会议(自定义) |
| 设置复杂度 | 中等 |
| 官方集成 | 否 |
| 同步数据 | 参与者、事件、网络研讨会、联系人 |
| 认证方式 | OAuth 2.0 / 服务器对服务器 OAuth |
功能
- 参与者同步 - 从会议参与者自动创建 Brevo 联系人
- 网络研讨会出席者捕获 - 同步网络研讨会注册者和出席者
- 会议事件触发 - 在会议开始、结束和录制事件时触发自动化
- 互动追踪 - 追踪出席时长和参与度指标
- 网络研讨会跟进 - 根据出席情况触发精准邮件序列
- 录制通知 - 通过 Brevo 邮件活动发送录制链接
前提条件
开始之前,请确保您已具备:
- Zoom 账户(专业版或以上)
- 通过 Zoom 应用市场 创建的 Zoom 服务器对服务器 OAuth 应用或 OAuth 应用
- 具有 API 访问权限的 Brevo 账户
- 具有连接器权限的 Tajo 账户
认证
服务器对服务器 OAuth(推荐)
# Create a Server-to-Server OAuth app at marketplace.zoom.usexport ZOOM_ACCOUNT_ID=your_account_idexport ZOOM_CLIENT_ID=your_client_idexport ZOOM_CLIENT_SECRET=your_client_secret// Get access token via Server-to-Server OAuthconst 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(用户级)
// Authorization URL for user-level OAuthconst 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 tokensconst 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' })});配置
基础设置
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: email: email name: FIRSTNAME join_time: MEETING_JOIN_DATE duration: MEETING_DURATION webinar_title: WEBINAR_NAME attendance_status: ATTENDANCE_STATUS registration_source: UTM_SOURCEAPI 端点
| 端点 | 方法 | 描述 |
|---|---|---|
https://api.zoom.us/v2/users | GET | 列出用户 |
https://api.zoom.us/v2/users/{userId}/meetings | GET | 列出会议 |
https://api.zoom.us/v2/meetings/{meetingId} | GET | 获取会议详情 |
https://api.zoom.us/v2/past_meetings/{meetingId}/participants | GET | 列出历史会议参与者 |
https://api.zoom.us/v2/users/{userId}/webinars | GET | 列出网络研讨会 |
https://api.zoom.us/v2/webinars/{webinarId}/registrants | GET | 列出网络研讨会注册者 |
https://api.zoom.us/v2/webinars/{webinarId}/participants | GET | 列出网络研讨会参与者 |
https://api.zoom.us/v2/meetings/{meetingId}/recordings | GET | 获取会议录制 |
https://api.zoom.us/v2/webhooks | POST | 订阅 Webhook |
代码示例
初始化连接器
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});同步会议参与者
// Retrieve past meeting participantsconst 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] }); }}同步网络研讨会出席者
// Get webinar attendees and sync to Brevoconst 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] });}处理 Zoom Webhook
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');});速率限制
| 类别 | 限制 | 说明 |
|---|---|---|
| 轻量 API 调用 | 30 请求/秒 | 获取用户、会议信息 |
| 中量 API 调用 | 20 请求/秒 | 列出参与者、网络研讨会 |
| 重量 API 调用 | 10 请求/秒 | 报告、录制 |
| 每日限制 | 5,000+ | 取决于套餐级别 |
速率限制头部
Zoom 返回 X-RateLimit-Limit、X-RateLimit-Remaining 和 Retry-After 头部。根据这些头部实施退避逻辑以避免 429 错误。
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 401 Unauthorized | 令牌已过期 | 刷新服务器对服务器 OAuth 令牌 |
| 参与者缺失 | 会议尚未结束 | 等待会议结束后获取完整数据 |
| Webhook 验证失败 | 密钥错误 | 在 Zoom 应用市场中验证 Webhook 密钥 |
| 无邮箱数据 | 访客参与者 | 启用注册以捕获邮箱 |
| 速率限制 429 | 请求过多 | 实施指数退避 |
调试模式
connectors: zoom: debug: true log_level: verbose log_webhooks: true最佳实践
- 使用服务器对服务器 OAuth - 无需用户交互的更简单认证
- 启用网络研讨会注册 - 捕获出席者邮箱地址所必需
- 会议结束后处理 - 参与者数据只在会议结束后才完整
- 按事件类型细分 - 为会议和网络研讨会分配不同的 Brevo 列表
- 追踪互动指标 - 使用时长和加入时间进行线索评分
- 发送录制跟进 - 通过 Brevo 自动化投递录制链接
安全
- OAuth 2.0 - 服务器对服务器或用户级 OAuth 认证
- Webhook 验证 - HMAC-SHA256 签名验证
- URL 验证 - Webhook 端点的挑战响应验证
- 范围权限 - 请求最低所需的 OAuth 范围
- 令牌轮换 - 服务器对服务器令牌自动过期(1 小时)
- 加密传输 - 所有 API 通信使用 TLS 1.2+