요약
Calendly API를 사용하면 예약 워크플로우를 자동화할 수 있습니다. OAuth 2.0으로 인증하고, api.calendly.com을 통해 이벤트 유형과 예약 정보를 조회하며, 웹훅으로 실시간 업데이트를 받을 수 있습니다. 테스트 시 Apidog로 웹훅 페이로드를 검증하고 실제 예약 없이 통합을 검증할 수 있습니다.
소개
Calendly는 매달 수백만 회의 예약을 처리합니다. 영업, 지원, 상담, 인터뷰 등 다양한 목적에 사용되며, API를 통해 앱에 예약 기능을 통합할 수 있습니다.
가장 흔한 패턴은 Calendly 예약이 시스템 내 작업을 트리거하는 것입니다. 예를 들어, 데모 예약 시 CRM 업데이트, 상담 예약 시 설문지 발송, 회의 취소 시 팀 알림 등입니다.
Calendly API는 웹훅으로 이를 지원합니다. 이벤트(예약 생성/취소/재조정) 발생 시 지정한 엔드포인트로 POST 요청을 보내고, 페이로드를 처리해 원하는 작업을 구현할 수 있습니다.
💡 예약 통합을 개발한다면, Apidog는 웹훅 핸들러 테스트와 페이로드 검증에 유용합니다. 실제 캘린더 연결 전에 다양한 이벤트 유형을 쉽게 모의할 수 있습니다.
OAuth 2.0을 사용한 인증
Calendly 개발자 문서 참고. Calendly API는 반드시 OAuth 2.0 인증이 필요합니다.
OAuth 애플리케이션 생성
- Calendly → 통합(Integrations) → API & 웹훅(Webhooks)으로 이동
- “새 애플리케이션 생성(Create New Application)” 클릭
- 리다이렉트 URI 설정 (예:
https://yourapp.com/auth/calendly/callback) - 클라이언트 ID/시크릿 획득
OAuth 인증 흐름
1단계: 사용자 권한 요청 리다이렉트
https://auth.calendly.com/oauth/authorize?
client_id=YOUR_CLIENT_ID&
response_type=code&
redirect_uri=https://yourapp.com/auth/calendly/callback
2단계: 사용자 인증 후 콜백
https://yourapp.com/auth/calendly/callback?code=AUTHORIZATION_CODE
3단계: 인증 코드로 토큰 교환
const response = await fetch('https://auth.calendly.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64')
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: authCode,
redirect_uri: 'https://yourapp.com/auth/calendly/callback'
})
})
const { access_token, refresh_token, expires_in } = await response.json()
4단계: API 호출에 토큰 적용
curl -X GET "https://api.calendly.com/users/me" \
-H "Authorization: Bearer ACCESS_TOKEN"
리프레시 토큰 사용
액세스 토큰은 2시간 후 만료됩니다. 리프레시 토큰으로 갱신하세요.
const response = await fetch('https://auth.calendly.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64')
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: storedRefreshToken
})
})
사용자 정보 가져오기
현재 사용자 확인
curl -X GET "https://api.calendly.com/users/me" \
-H "Authorization: Bearer ACCESS_TOKEN"
샘플 응답
{
"resource": {
"avatar_url": "https://calendly.com/avatar.jpg",
"created_at": "2024-01-15T10:00:00Z",
"current_organization": "https://api.calendly.com/organizations/ABC123",
"email": "you@example.com",
"name": "John Doe",
"scheduling_url": "https://calendly.com/johndoe",
"slug": "johndoe",
"timezone": "America/New_York",
"uri": "https://api.calendly.com/users/ABC123"
}
}
조직 멤버십 확인
curl -X GET "https://api.calendly.com/organization_memberships/me" \
-H "Authorization: Bearer ACCESS_TOKEN"
이벤트 유형
이벤트 유형은 회의 템플릿(예: 30분 상담, 60분 미팅 등)입니다.
이벤트 유형 목록 조회
curl -X GET "https://api.calendly.com/event_types?user=https://api.calendly.com/users/ABC123" \
-H "Authorization: Bearer ACCESS_TOKEN"
응답 예시
{
"resource": {
"uri": "https://api.calendly.com/event_types/ETC123",
"active": true,
"booking_method": "instant",
"color": "#0066FF",
"created_at": "2024-01-15T10:00:00Z",
"description_html": "<p>30-minute consultation</p>",
"duration": 30,
"internal_note": "Use Zoom link",
"kind": "solo",
"name": "30 Min Consultation",
"pooling_type": null,
"profile": {
"name": "John Doe",
"type": "User",
"owner": "https://api.calendly.com/users/ABC123"
},
"scheduling_url": "https://calendly.com/johndoe/30min",
"slug": "30min",
"type": "StandardEventType"
},
"pagination": {
"count": 1,
"next_page": null
}
}
특정 이벤트 유형 조회
curl -X GET "https://api.calendly.com/event_types/ETC123" \
-H "Authorization: Bearer ACCESS_TOKEN"
예약된 이벤트(예약)
이벤트는 실제로 예약된 일정입니다.
예약된 이벤트 목록
curl -X GET "https://api.calendly.com/scheduled_events?user=https://api.calendly.com/users/ABC123" \
-H "Authorization: Bearer ACCESS_TOKEN"
날짜 범위 필터
curl -X GET "https://api.calendly.com/scheduled_events?min_start_time=2026-03-01T00:00:00Z&max_start_time=2026-03-31T23:59:59Z" \
-H "Authorization: Bearer ACCESS_TOKEN"
응답 예시
{
"resource": {
"uri": "https://api.calendly.com/scheduled_events/ABC123",
"status": "active",
"tracking": {
"utm_campaign": "spring_sale",
"utm_source": "email",
"utm_medium": "newsletter"
},
"created_at": "2026-03-24T10:00:00Z",
"end_time": "2026-03-25T11:00:00Z",
"event_type": "https://api.calendly.com/event_types/ETC123",
"invitees_counter": {
"active": 1,
"limit": 1,
"total": 1
},
"location": {
"type": "zoom",
"join_url": "https://zoom.us/j/123456789"
},
"start_time": "2026-03-25T10:30:00Z",
"updated_at": "2026-03-24T10:00:00Z"
}
}
이벤트 세부 정보
curl -X GET "https://api.calendly.com/scheduled_events/EVENT_ID" \
-H "Authorization: Bearer ACCESS_TOKEN"
이벤트 초대자 목록
curl -X GET "https://api.calendly.com/scheduled_events/EVENT_ID/invitees" \
-H "Authorization: Bearer ACCESS_TOKEN"
응답 예시
{
"resource": [
{
"cancel_url": "https://calendly.com/cancellations/ABC123",
"created_at": "2026-03-24T10:00:00Z",
"email": "jane@example.com",
"event": "https://api.calendly.com/scheduled_events/ABC123",
"name": "Jane Smith",
"new_invitee": null,
"old_invitee": null,
"reschedule_url": "https://calendly.com/reschedulings/ABC123",
"status": "active",
"text_reminder_number": "+15551234567",
"timezone": "America/New_York",
"tracking": {
"utm_campaign": null,
"utm_source": null
},
"updated_at": "2026-03-24T10:00:00Z",
"uri": "https://api.calendly.com/scheduled_event_invitees/INV123",
"canceled": null
}
]
}
실시간 업데이트를 위한 웹훅
웹훅으로 예약 이벤트를 실시간 수신할 수 있습니다.
웹훅 구독 생성
curl -X POST "https://api.calendly.com/webhook_subscriptions" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/calendly",
"events": [
"invitee.created",
"invitee.canceled",
"invitee.rescheduled"
],
"organization": "https://api.calendly.com/organizations/ORG123",
"scope": "organization"
}'
구독 가능한 이벤트
-
invitee.created– 새 예약 생성 -
invitee.canceled– 예약 취소 -
invitee.rescheduled– 예약 재조정
웹훅 구독 목록
curl -X GET "https://api.calendly.com/webhook_subscriptions?organization=https://api.calendly.com/organizations/ORG123" \
-H "Authorization: Bearer ACCESS_TOKEN"
웹훅 삭제
curl -X DELETE "https://api.calendly.com/webhook_subscriptions/WEBHOOK_ID" \
-H "Authorization: Bearer ACCESS_TOKEN"
웹훅 페이로드 처리
웹훅 서명 검증
Calendly는 Calendly-Webhook-Signature 헤더로 웹훅에 서명을 추가합니다.
import crypto from 'crypto'
function verifySignature(payload, signature, secret) {
const [t, v1] = signature.split(',')
const timestamp = t.split('=')[1]
const hash = v1.split('=')[1]
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + payload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(hash),
Buffer.from(expectedSignature)
)
}
app.post('/webhooks/calendly', (req, res) => {
const signature = req.headers['calendly-webhook-signature']
const payload = JSON.stringify(req.body)
if (!verifySignature(payload, signature, process.env.CALENDLY_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
// 웹훅 처리
handleWebhook(req.body)
res.status(200).send('OK')
})
예약 이벤트 처리 예시
function handleWebhook(payload) {
const { event, payload: data } = payload
switch (event) {
case 'invitee.created':
console.log(`New booking: ${data.event.start_time}`)
console.log(`Invitee: ${data.email}`)
// CRM 연동, 확인 메일 발송 등
syncToCRM(data)
break
case 'invitee.canceled':
console.log(`Booking canceled: ${data.event.uri}`)
// CRM 업데이트, 팀 알림 등
removeFromCRM(data)
break
case 'invitee.rescheduled':
console.log(`Booking rescheduled: ${data.event.start_time}`)
// 캘린더, CRM 동기화 등
updateCRM(data)
break
}
}
Apidog로 테스트하기
Calendly API는 OAuth 인증이 필수라 테스트가 번거롭습니다. Apidog를 사용하면 테스트가 훨씬 간단해집니다.
1. OAuth 응답 Mock
개발 중에는 매번 OAuth 플로우를 거치지 말고, 아래와 같이 토큰 응답을 모의(mock)하세요.
{
"access_token": "mock_access_token",
"refresh_token": "mock_refresh_token",
"expires_in": 7200,
"created_at": 1700000000
}
2. 웹훅 핸들러 테스트
아래와 같이 웹훅 페이로드를 생성해서 엔드포인트에 전송하고 처리를 확인하세요.
{
"created_at": "2026-03-24T10:00:00Z",
"event": "invitee.created",
"payload": {
"email": "test@example.com",
"name": "Test User",
"event": {
"start_time": "2026-03-25T10:30:00Z",
"end_time": "2026-03-25T11:00:00Z",
"event_type": {
"name": "30 Min Consultation"
}
}
}
}
3. 환경 변수 관리
CALENDLY_CLIENT_ID: abc123
CALENDLY_CLIENT_SECRET: xyz789
CALENDLY_ACCESS_TOKEN: stored_token
CALENDLY_REFRESH_TOKEN: stored_refresh
CALENDLY_WEBHOOK_SECRET: webhook_signing_secret
4. 웹훅 서명 검증 테스트
pm.test('Webhook signature is valid', () => {
const signature = pm.request.headers.get('Calendly-Webhook-Signature')
pm.expect(signature).to.exist
const payload = pm.request.body.raw
const secret = pm.environment.get('CALENDLY_WEBHOOK_SECRET')
// Verify signature
const valid = verifySignature(payload, signature, secret)
pm.expect(valid).to.be.true
})
Apidog로 Calendly 웹훅 테스트 - 무료
일반적인 오류 및 해결 방법
401 권한 없음
원인: 토큰이 유효하지 않거나 만료됨
해결:
- 토큰 만료 여부 확인(2시간)
- 리프레시 토큰으로 새 액세스 토큰 획득
- Authorization 헤더가
Bearer {token}형태인지 확인
403 접근 금지
원인: OAuth 스코프 부족
해결:
- OAuth 권한 요청 시 필요한 스코프 포함
404 찾을 수 없음
원인: 리소스 존재하지 않음 또는 접근 권한 없음
해결:
- URI 정확성 확인
- 인증된 사용자 권한 확인
- 이벤트 유형/ID 유효성 확인
422 처리할 수 없는 엔티티
원인: 파라미터 유효성 오류
해결:
- 응답 메시지 확인
{
"title": "Validation Error",
"message": "Invalid parameter: url must be a valid HTTPS URL"
}
대안 및 비교
| 기능 | Calendly | Acuity | Cal.com | Calendly |
|---|---|---|---|---|
| 무료 요금제 | 제한적 | 제한적 | 자체 호스팅 무료 | ✓ |
| API 접근 | ✓ | ✓ | ✓ | ✓ |
| 웹훅 | ✓ | ✓ | ✓ | ✓ |
| OAuth | ✓ | API 키 | API 키 | OAuth |
| 팀 예약 | ✓ | ✓ | ✓ | ✓ |
| 오픈 소스 | 아니오 | 아니오 | 예 | 아니오 |
Calendly는 가장 잘 정비된 API 문서와 OAuth 구현을 제공합니다. Cal.com은 오픈 소스 및 간단한 API 키 인증을 지원하는 대안입니다.
실제 사용 사례
영업 CRM 통합
- B2B SaaS에서 가격 페이지에 Calendly 삽입
- 예약 시 웹훅으로 Salesforce 리드 생성, Slack 알림, 마케팅 자동화, 고객 성공 시스템에 기록 등
상담 플랫폼
- 고객이 변호사 등과 상담 예약
- 내부 예약 시스템 동기화, Zoom 링크 생성, 사전 설문지 발송, 회의 완료 시 사건 파일 생성
인터뷰 예약
- 채용 플랫폼에서 지원자 인터뷰 예약
- ATS(채용 시스템) 업데이트, 채용 담당자 이메일 알림, 모든 참석자에게 캘린더 초대, 불참자 추적
결론
핵심 요약:
- Calendly는 OAuth 2.0 기반 인증을 사용
- API로 이벤트 유형과 예약 정보에 접근
- 웹훅으로 실시간 예약 이벤트 수신
- 보안 위해 웹훅 서명 검증 필수
- Apidog로 실제 캘린더 연결 전 통합 테스트 권장
실행 단계:
- Calendly에서 OAuth 앱 생성
- OAuth 인증 플로우 구현
- 웹훅 구독 설정
- Apidog에서 모의 페이로드로 테스트
- 프로덕션 배포
Apidog로 Calendly 웹훅 테스트 - 무료
자주 묻는 질문 (FAQ)
Q. API 사용에 유료 요금제가 필요한가요?
A. 아니요. 모든 요금제(무료 포함)에서 API 사용 가능. 단, 무료 플랜은 일부 기능이 제한. 웹훅도 모두 지원.
Q. 사용자 수준 웹훅과 조직 수준 웹훅의 차이는?
A. 사용자 수준은 한 명의 이벤트만, 조직 수준은 모든 팀원 이벤트를 캡처합니다. 대부분은 조직 스코프 사용을 권장.
Q. 웹훅 서명 시크릿은 어떻게 얻나요?
A. API로 웹훅 생성 시 응답에 signing_key(서명키)가 포함됩니다. 안전하게 저장 후 서명 검증에 사용.
Q. API로 예약 생성이 가능한가요?
A. 불가능. 예약은 Calendly UI/위젯에서만 생성 가능. API는 읽기 전용.
Q. 시간대 변환은 어떻게 해야 하나요?
A. 모든 타임스탬프는 UTC(ISO 8601). 필요 시 애플리케이션에서 로컬로 변환. 사용자 타임존 정보는 사용자 리소스에서 제공.
Q. 속도 제한은?
A. 공식 문서에 명시되어 있지 않음. 요청이 많아지면 지수 백오프(Exponential Backoff) 구현 권장.
Q. 과거 예약 데이터도 조회 가능한가요?
A. 네. min_start_time 및 max_start_time 파라미터로 과거 일정 쿼리 가능. 기간 제한 없음.
Q. 로컬에서 OAuth 플로우 테스트 방법은?
A. ngrok 등 터널링 서비스로 로컬 서버를 외부에 노출, 리다이렉트 URI에 ngrok URL을 사용. 브라우저에서 인증 후 콜백 확인.

Top comments (0)