Calendly 커넥터

Tajo를 통해 Calendly를 Brevo에 연결하여 미팅 초대자를 자동으로 연락처로 동기화하고, 예약 이벤트 기반의 이메일 시퀀스를 트리거하며, 영업 및 온보딩 워크플로우를 효율화하세요.

개요

속성
플랫폼Calendly
카테고리Scheduling (Custom)
설정 난이도쉬움
공식 통합아니오
동기화되는 데이터이벤트, 연락처, 예약, 취소
인증 방식OAuth 2.0 / Personal Access Token

주요 기능

  • 초대자 동기화 - 미팅 초대자로부터 Brevo 연락처를 자동으로 생성합니다
  • 예약 트리거 - 미팅이 예약되면 Brevo 자동화를 실행합니다
  • 취소 처리 - 취소 시 재참여 플로우를 트리거합니다
  • 노쇼 감지 - 초대자가 미팅을 놓쳤을 때 연락처 상태를 업데이트합니다
  • 이벤트 타입 매핑 - 다양한 Calendly 이벤트 타입을 Brevo 리스트에 매핑합니다
  • Scheduling API - 리디렉션 없이 앱에 스케줄링을 직접 구축합니다

사전 준비 사항

시작하기 전에 다음 사항이 준비되어 있어야 합니다.

  1. Calendly 계정 (API 액세스를 위한 Professional 플랜 이상)
  2. Calendly Integrations에서 발급한 Personal Access Token
  3. API 액세스가 활성화된 Brevo 계정
  4. 커넥터 권한이 있는 Tajo 계정

인증

Personal Access Token

Terminal window
# Generate at https://calendly.com/integrations/api_webhooks
export CALENDLY_ACCESS_TOKEN=your_personal_access_token
export TAJO_API_KEY=your_tajo_api_key
export BREVO_API_KEY=your_brevo_api_key

OAuth 2.0

// OAuth 2.0 Authorization Code Flow
const 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 token
const 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_STATUS

API 엔드포인트

엔드포인트Method설명
https://api.calendly.com/users/meGET현재 사용자 조회
https://api.calendly.com/event_typesGET이벤트 타입 목록
https://api.calendly.com/scheduled_eventsGET예약된 이벤트 목록
https://api.calendly.com/scheduled_events/{uuid}GET예약된 이벤트 조회
https://api.calendly.com/scheduled_events/{uuid}/inviteesGET초대자 목록
https://api.calendly.com/scheduling_linksPOST스케줄링 링크 생성
https://api.calendly.com/webhook_subscriptionsPOST웹훅 생성
https://api.calendly.com/webhook_subscriptionsGET웹훅 목록
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 events
const 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 Brevo
const 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 events
const 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.tajo.io/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
})
}
);

웹훅 이벤트 처리

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

모범 사례

  1. 웹훅 사용 - 실시간 동기화를 위해 invitee.createdinvitee.canceled를 구독하세요
  2. 커스텀 질문 추가 - 더 풍부한 연락처 프로필을 위해 회사, 역할, 전화번호 데이터를 수집하세요
  3. 이벤트 타입 매핑 - Calendly 이벤트 타입별로 다른 Brevo 리스트를 할당하세요
  4. 노쇼 처리 - 리드 스코어링과 후속 시퀀스를 조정하기 위해 노쇼를 추적하세요
  5. 스케줄링 링크 사용 - 맞춤형 예약 경험을 위해 고유 스케줄링 링크를 생성하세요
  6. 조직 스코프 설정 - 모든 팀원의 이벤트를 캡처하려면 조직 수준 웹훅을 사용하세요

보안

  • OAuth 2.0 - 스코프 기반 토큰 인증
  • 웹훅 서명 - 수신 웹훅에 대한 HMAC 서명 검증
  • HTTPS 전용 - 모든 API 엔드포인트는 TLS 암호화가 필요합니다
  • 토큰 만료 - OAuth 토큰은 만료되며 새로 고침 플로우가 필요합니다
  • 최소 스코프 - 필요한 OAuth 스코프만 요청하세요
  • 안전한 저장 - 환경 변수 또는 시크릿 매니저에 토큰을 저장하세요

관련 리소스

Subscribe to updates

developer-docs

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

auto-detect
AI 어시스턴트

안녕하세요! 문서에 대해 무엇이든 물어보세요.