Calendly 커넥터
커넥터 상태: 예정 아직 구축되지 않았습니다. Tajo는 현재 이 커넥터를 실행하지 않습니다.
예시 그림 · 샘플 데이터
동기화 예시
원본
Calendly
커넥터 상태: 예정
대상
다른 도구
커넥터 상태: 사용자 지정 도구
필드 매핑
| 원본 필드 | 대상 필드 | 예시 값 |
|---|---|---|
record.id | source_id | record_1042 |
record.updated_at | updated_at | 2026-10-18T08:00:00Z |
record.value | value | sample_value |
- 초안
- 레코드 한 건 검토
- 이 버전 승인
상태는 개별 커넥터에 대한 것으로, 이 동기화가 준비됐다는 보장은 아닙니다. 표시된 필드는 예시입니다. 동기화 실행 전에 실제 스키마와 권한을 확인하세요.
Tajo를 통해 Calendly를 Brevo에 연결하여 미팅 초대자를 자동으로 연락처로 동기화하고, 예약 이벤트 기반의 이메일 시퀀스를 트리거하며, 영업 및 온보딩 워크플로우를 효율화하세요.
개요
| 속성 | 값 |
|---|---|
| 플랫폼 | Calendly |
| 카테고리 | Scheduling (Custom) |
| 설정 난이도 | 쉬움 |
| 공식 통합 | 아니오 |
| 동기화되는 데이터 | 이벤트, 연락처, 예약, 취소 |
| 인증 방식 | OAuth 2.0 / Personal Access Token |
주요 기능
- 초대자 동기화 - 미팅 초대자로부터 Brevo 연락처를 자동으로 생성합니다
- 예약 트리거 - 미팅이 예약되면 Brevo 자동화를 실행합니다
- 취소 처리 - 취소 시 재참여 플로우를 트리거합니다
- 노쇼 감지 - 초대자가 미팅을 놓쳤을 때 연락처 상태를 업데이트합니다
- 이벤트 타입 매핑 - 다양한 Calendly 이벤트 타입을 Brevo 리스트에 매핑합니다
- Scheduling API - 리디렉션 없이 앱에 스케줄링을 직접 구축합니다
사전 준비 사항
시작하기 전에 다음 사항이 준비되어 있어야 합니다.
- Calendly 계정 (API 액세스를 위한 Professional 플랜 이상)
- Calendly Integrations에서 발급한 Personal Access Token
- API 액세스가 활성화된 Brevo 계정
- 커넥터 권한이 있는 Tajo 계정
인증
Personal Access Token
# Generate at https://calendly.com/integrations/api_webhooksexport CALENDLY_ACCESS_TOKEN=your_personal_access_tokenexport TAJO_API_KEY=your_tajo_api_keyexport BREVO_API_KEY=your_brevo_api_keyOAuth 2.0
// OAuth 2.0 Authorization Code Flowconst authUrl = 'https://auth.calendly.com/oauth/authorize?' + new URLSearchParams({ client_id: process.env.CALENDLY_CLIENT_ID, redirect_uri: 'https://your-app.com/callback', response_type: 'code' });
// Exchange code for tokenconst tokenResponse = await fetch('https://auth.calendly.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: authorizationCode, client_id: process.env.CALENDLY_CLIENT_ID, client_secret: process.env.CALENDLY_CLIENT_SECRET, redirect_uri: 'https://your-app.com/callback' })});설정
기본 설정
connectors: calendly: enabled: true access_token: "${CALENDLY_ACCESS_TOKEN}"
sync: contacts: true events: true cancellations: true
event_mapping: discovery_call: list_id: 10 event_type_uri: "https://api.calendly.com/event_types/abc123" demo: list_id: 11 event_type_uri: "https://api.calendly.com/event_types/xyz789"
webhook: signing_key: "${CALENDLY_WEBHOOK_SIGNING_KEY}"필드 매핑
field_mapping: email: email name: FIRSTNAME questions_and_answers: company: COMPANY role: JOB_TITLE phone: SMS event_type_name: CALENDLY_EVENT_TYPE scheduled_at: MEETING_DATE status: BOOKING_STATUSAPI 엔드포인트
| 엔드포인트 | Method | 설명 |
|---|---|---|
https://api.calendly.com/users/me | GET | 현재 사용자 조회 |
https://api.calendly.com/event_types | GET | 이벤트 타입 목록 |
https://api.calendly.com/scheduled_events | GET | 예약된 이벤트 목록 |
https://api.calendly.com/scheduled_events/{uuid} | GET | 예약된 이벤트 조회 |
https://api.calendly.com/scheduled_events/{uuid}/invitees | GET | 초대자 목록 |
https://api.calendly.com/scheduling_links | POST | 스케줄링 링크 생성 |
https://api.calendly.com/webhook_subscriptions | POST | 웹훅 생성 |
https://api.calendly.com/webhook_subscriptions | GET | 웹훅 목록 |
https://api.calendly.com/invitee_no_shows/{uuid} | GET | 노쇼 상태 조회 |
코드 예제
커넥터 초기화
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('calendly', { accessToken: process.env.CALENDLY_ACCESS_TOKEN});예약된 이벤트 목록 조회
// Retrieve scheduled eventsconst response = await fetch( 'https://api.calendly.com/scheduled_events?' + new URLSearchParams({ user: 'https://api.calendly.com/users/YOUR_USER_ID', min_start_time: '2024-01-01T00:00:00Z', max_start_time: '2024-12-31T23:59:59Z', status: 'active', count: 100 }), { headers: { 'Authorization': `Bearer ${process.env.CALENDLY_ACCESS_TOKEN}`, 'Content-Type': 'application/json' } });
const events = await response.json();초대자를 Brevo로 동기화
// Get invitees for a scheduled event and sync to Brevoconst inviteesResponse = await fetch( `https://api.calendly.com/scheduled_events/${eventUuid}/invitees`, { headers: { 'Authorization': `Bearer ${process.env.CALENDLY_ACCESS_TOKEN}` } });
const { collection } = await inviteesResponse.json();
for (const invitee of collection) { await tajo.contacts.sync({ email: invitee.email, attributes: { FIRSTNAME: invitee.name, CALENDLY_EVENT_TYPE: invitee.event, MEETING_DATE: invitee.created_at, BOOKING_STATUS: invitee.status }, listIds: [10] });}웹훅 구독 설정
// Subscribe to Calendly eventsconst webhook = await fetch( 'https://api.calendly.com/webhook_subscriptions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CALENDLY_ACCESS_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://api.example.com/webhooks/calendly', events: [ 'invitee.created', 'invitee.canceled', 'invitee_no_show.created' ], organization: 'https://api.calendly.com/organizations/YOUR_ORG_ID', scope: 'organization', signing_key: process.env.CALENDLY_WEBHOOK_SIGNING_KEY }) });Caution
위의 example.com 주소는 Tajo 엔드포인트가 제공될 때까지 사용하는 자리표시자입니다.
웹훅 이벤트 처리
app.post('/webhooks/calendly', async (req, res) => { // Verify webhook signature const signature = req.headers['calendly-webhook-signature']; const isValid = verifyCalendlySignature( req.rawBody, signature, process.env.CALENDLY_WEBHOOK_SIGNING_KEY );
if (!isValid) return res.status(401).send('Unauthorized');
const { event, payload } = req.body;
switch (event) { case 'invitee.created': await tajo.contacts.sync({ email: payload.email, attributes: { BOOKING_STATUS: 'booked' }, listIds: [10] }); break; case 'invitee.canceled': await tajo.contacts.update(payload.email, { attributes: { BOOKING_STATUS: 'cancelled' } }); break; case 'invitee_no_show.created': await tajo.contacts.update(payload.email, { attributes: { BOOKING_STATUS: 'no_show' } }); break; }
res.status(200).send('OK');});요청 제한
| 리소스 | 제한 | 비고 |
|---|---|---|
| API 요청 | 분당 6,000회 | 조직 전체 제한 |
| 웹훅 구독 | 조직당 30개 | 모든 이벤트 타입 합산 |
| 스케줄링 링크 | 무제한 | 분당 제한 없음 |
페이지네이션
Calendly API 응답은 커서 기반 페이지네이션을 사용합니다. pagination 객체의 next_page_token을 사용해 추가 결과를 가져오세요. 기본 페이지 크기는 20개 항목이며, 최대 100개입니다.
문제 해결
| 문제 | 원인 | 해결 방법 |
|---|---|---|
| 웹훅 수신 안 됨 | 잘못된 스코프 | 웹훅에 organization 스코프를 사용하세요 |
| 401 Unauthorized | 토큰 만료 | 새 토큰을 생성하거나 OAuth 토큰을 새로 고치세요 |
| 초대자 데이터 누락 | 질문이 구성되지 않음 | 이벤트 타입에 커스텀 질문을 추가하세요 |
| 중복 연락처 | 중복 제거 로직 없음 | upsert의 고유 식별자로 이메일을 사용하세요 |
| Rate limit 429 | 요청이 너무 많음 | 백오프와 요청 배치를 구현하세요 |
디버그 모드
connectors: calendly: debug: true log_level: verbose log_webhooks: true모범 사례
- 웹훅 사용 - 실시간 동기화를 위해
invitee.created와invitee.canceled를 구독하세요 - 커스텀 질문 추가 - 더 풍부한 연락처 프로필을 위해 회사, 역할, 전화번호 데이터를 수집하세요
- 이벤트 타입 매핑 - Calendly 이벤트 타입별로 다른 Brevo 리스트를 할당하세요
- 노쇼 처리 - 리드 스코어링과 후속 시퀀스를 조정하기 위해 노쇼를 추적하세요
- 스케줄링 링크 사용 - 맞춤형 예약 경험을 위해 고유 스케줄링 링크를 생성하세요
- 조직 스코프 설정 - 모든 팀원의 이벤트를 캡처하려면 조직 수준 웹훅을 사용하세요
보안
- OAuth 2.0 - 스코프 기반 토큰 인증
- 웹훅 서명 - 수신 웹훅에 대한 HMAC 서명 검증
- HTTPS 전용 - 모든 API 엔드포인트는 TLS 암호화가 필요합니다
- 토큰 만료 - OAuth 토큰은 만료되며 새로 고침 플로우가 필요합니다
- 최소 스코프 - 필요한 OAuth 스코프만 요청하세요
- 안전한 저장 - 환경 변수 또는 시크릿 매니저에 토큰을 저장하세요