Zoom 커넥터
Tajo를 통해 Zoom을 Brevo에 연결하여 회의 참가자와 웨비나 참석자를 연락처로 자동 동기화하고, 회의 후 후속 조치 시퀀스를 트리거하며, 마케팅 자동화의 참여 지표를 추적하십시오.
개요
| 속성 | 값 |
|---|---|
| 플랫폼 | Zoom |
| 카테고리 | 화상 회의 (Custom) |
| 설정 복잡도 | 중간 |
| 공식 통합 | 아니오 |
| 동기화 데이터 | 참가자, 이벤트, 웨비나, 연락처 |
| 인증 방법 | OAuth 2.0 / Server-to-Server OAuth |
기능
- 참가자 동기화 - 회의 참가자로부터 Brevo 연락처 자동 생성
- 웨비나 참석자 캡처 - 웨비나 등록자와 참석자 동기화
- 회의 이벤트 트리거 - 회의 시작, 종료, 녹화 이벤트에 자동화 실행
- 참여 추적 - 참석 시간 및 참여 지표 추적
- 웨비나 후속 조치 - 웨비나 참석 기반 타겟 이메일 시퀀스 트리거
- 녹화 알림 - Brevo 이메일 캠페인을 통해 녹화 링크 전송
사전 요구 사항
시작하기 전에 다음이 준비되어 있는지 확인하십시오.
- Zoom 계정 (Pro 요금제 이상)
- Zoom App Marketplace를 통한 Zoom Server-to-Server OAuth 앱 또는 OAuth 앱
- API 접근이 가능한 Brevo 계정
- 커넥터 권한이 있는 Tajo 계정
인증
Server-to-Server OAuth (권장)
# marketplace.zoom.us 에서 Server-to-Server OAuth 앱 생성export ZOOM_ACCOUNT_ID=your_account_idexport ZOOM_CLIENT_ID=your_client_idexport ZOOM_CLIENT_SECRET=your_client_secret// 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 (사용자 수준)
// 사용자 수준 OAuth용 인증 URLconst 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' });
// 코드를 토큰으로 교환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' })});구성
기본 설정
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 | 웹훅 구독 |
코드 예제
커넥터 초기화
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});회의 참가자 동기화
// 과거 회의 참가자 가져오기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] }); }}웨비나 참석자 동기화
// 웨비나 참석자를 가져와서 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] });}Zoom 웹훅 처리
app.post('/webhooks/zoom', async (req, res) => { // Zoom URL 유효성 검사 챌린지 처리 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 }); }
// 웹훅 서명 확인 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 요청/초 | GET 사용자, 회의 정보 |
| 중량 API 호출 | 20 요청/초 | 참가자, 웨비나 목록 |
| 고부하 API 호출 | 10 요청/초 | 보고서, 녹화 |
| 일일 한도 | 5,000+ | 요금제 수준에 따라 다름 |
속도 제한 헤더
Zoom은 X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After 헤더를 반환합니다. 429 오류를 방지하려면 이러한 헤더를 기반으로 백오프 로직을 구현하십시오.
문제 해결
| 문제 | 원인 | 해결 방법 |
|---|---|---|
| 401 Unauthorized | 토큰 만료 | Server-to-Server OAuth 토큰 새로 고침 |
| 참가자 누락 | 회의가 종료되지 않음 | 전체 데이터를 위해 회의가 종료될 때까지 대기 |
| 웹훅 유효성 검사 실패 | 잘못된 시크릿 | Zoom Marketplace에서 웹훅 시크릿 확인 |
| 이메일 데이터 없음 | 게스트 참가자 | 이메일 캡처를 위해 등록 활성화 |
| 속도 제한 429 | 너무 많은 요청 | 지수 백오프 구현 |
디버그 모드
connectors: zoom: debug: true log_level: verbose log_webhooks: true모범 사례
- Server-to-Server OAuth 사용 - 사용자 상호작용 없이 간단한 인증
- 웨비나 등록 활성화 - 참석자 이메일 주소 캡처에 필요
- 회의 종료 후 처리 - 참가자 데이터는 회의 종료 후에만 완전함
- 이벤트 유형별 세그먼트화 - 회의와 웨비나에 다른 Brevo 목록 할당
- 참여 지표 추적 - 리드 점수 산출에 시간 및 참여 시각 사용
- 녹화 후속 조치 전송 - Brevo를 통해 녹화 링크 배달 자동화
보안
- OAuth 2.0 - Server-to-Server 또는 사용자 수준 OAuth 인증
- 웹훅 확인 - HMAC-SHA256 서명 검증
- URL 유효성 검사 - 웹훅 엔드포인트에 대한 챌린지-응답 확인
- 범위 지정 권한 - 최소 필요한 OAuth 범위 요청
- 토큰 교체 - Server-to-Server 토큰 자동 만료 (1시간)
- 암호화된 전송 - 모든 API 통신에 TLS 1.2+ 사용