TÓM TẮT
API Calendly cho phép bạn tự động hóa quy trình lên lịch. Xác thực bằng OAuth 2.0, truy cập các loại sự kiện, đặt lịch qua api.calendly.com và nhận cập nhật thời gian thực qua webhooks. Để kiểm thử, hãy sử dụng Apidog để xác thực payload webhook và kiểm tra tích hợp mà không cần tạo lịch thật.
Giới thiệu
Calendly xử lý hàng triệu cuộc họp mỗi tháng cho bán hàng, hỗ trợ, tư vấn, phỏng vấn. API cho phép bạn nhúng khả năng lên lịch vào ứng dụng riêng.
Mô hình phổ biến: khi có đặt lịch Calendly, hệ thống của bạn xử lý tự động (ví dụ: cập nhật CRM, gửi email, thông báo nhóm). API Calendly hỗ trợ thực hiện thông qua webhook — khi có sự kiện (tạo, hủy, reschedule), Calendly sẽ POST đến endpoint của bạn để bạn xử lý.
💡Nếu bạn xây dựng tích hợp lên lịch, Apidog giúp kiểm thử trình xử lý webhook, xác thực payload. Bạn có thể mô phỏng phản hồi Calendly trong quá trình phát triển để đảm bảo tích hợp hoạt động đúng.
Xác thực với OAuth 2.0
Calendly sử dụng OAuth 2.0 cho API, không hỗ trợ khóa API thuần.
Tạo ứng dụng OAuth
- Vào Calendly → Tích hợp → API & Webhooks
- Nhấp “Tạo ứng dụng mới”
- Đặt URI chuyển hướng (ví dụ:
https://yourapp.com/auth/calendly/callback) - Lấy client ID và client secret
Luồng OAuth
Bước 1: Chuyển hướng người dùng để ủy quyền
https://auth.calendly.com/oauth/authorize?
client_id=YOUR_CLIENT_ID&
response_type=code&
redirect_uri=https://yourapp.com/auth/calendly/callback
Bước 2: Người dùng ủy quyền và được chuyển hướng về
https://yourapp.com/auth/calendly/callback?code=AUTHORIZATION_CODE
Bước 3: Đổi mã lấy access token
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()
Bước 4: Sử dụng token
curl -X GET "https://api.calendly.com/users/me" \
-H "Authorization: Bearer ACCESS_TOKEN"
Refresh token
Access token hết hạn sau 2 giờ. Dùng refresh token để lấy token mới:
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
})
})
Lấy thông tin người dùng
Lấy người dùng hiện tại
curl -X GET "https://api.calendly.com/users/me" \
-H "Authorization: Bearer ACCESS_TOKEN"
Phản hồi:
{
"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"
}
}
Lấy tư cách thành viên tổ chức
curl -X GET "https://api.calendly.com/organization_memberships/me" \
-H "Authorization: Bearer ACCESS_TOKEN"
Loại sự kiện
Loại sự kiện là các mẫu (template) cuộc họp mà người dùng tạo (30 phút, tư vấn 60 phút,...).
Liệt kê các loại sự kiện
curl -X GET "https://api.calendly.com/event_types?user=https://api.calendly.com/users/ABC123" \
-H "Authorization: Bearer ACCESS_TOKEN"
Phản hồi:
{
"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
}
}
Lấy một loại sự kiện cụ thể
curl -X GET "https://api.calendly.com/event_types/ETC123" \
-H "Authorization: Bearer ACCESS_TOKEN"
Sự kiện đã lên lịch (đặt lịch)
Các sự kiện là các booking thực tế được tạo qua Calendly.
Liệt kê các sự kiện đã lên lịch
curl -X GET "https://api.calendly.com/scheduled_events?user=https://api.calendly.com/users/ABC123" \
-H "Authorization: Bearer ACCESS_TOKEN"
Lọc theo khoảng thời gian:
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"
Phản hồi:
{
"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"
}
}
Lấy chi tiết sự kiện
curl -X GET "https://api.calendly.com/scheduled_events/EVENT_ID" \
-H "Authorization: Bearer ACCESS_TOKEN"
Lấy người được mời cho một sự kiện
curl -X GET "https://api.calendly.com/scheduled_events/EVENT_ID/invitees" \
-H "Authorization: Bearer ACCESS_TOKEN"
Phản hồi:
{
"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
}
]
}
Webhooks để cập nhật thời gian thực
Webhooks giúp ứng dụng nhận thông báo về các sự kiện đặt lịch.
Tạo đăng ký webhook
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"
}'
Các sự kiện có sẵn:
-
invitee.created- Đặt lịch mới được tạo -
invitee.canceled- Đặt lịch bị hủy -
invitee.rescheduled- Đặt lịch được lên lịch lại
Liệt kê các đăng ký webhook
curl -X GET "https://api.calendly.com/webhook_subscriptions?organization=https://api.calendly.com/organizations/ORG123" \
-H "Authorization: Bearer ACCESS_TOKEN"
Xóa một webhook
curl -X DELETE "https://api.calendly.com/webhook_subscriptions/WEBHOOK_ID" \
-H "Authorization: Bearer ACCESS_TOKEN"
Xử lý payload webhook
Xác minh chữ ký webhook
Calendly ký webhook bằng header 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')
}
// Xử lý webhook
handleWebhook(req.body)
res.status(200).send('OK')
})
Xử lý các sự kiện đặt lịch
function handleWebhook(payload) {
const { event, payload: data } = payload
switch (event) {
case 'invitee.created':
console.log(`Đặt lịch mới: ${data.event.start_time}`)
console.log(`Người được mời: ${data.email}`)
// Thêm vào CRM, gửi email xác nhận...
syncToCRM(data)
break
case 'invitee.canceled':
console.log(`Đặt lịch bị hủy: ${data.event.uri}`)
// Cập nhật CRM, thông báo nhóm...
removeFromCRM(data)
break
case 'invitee.rescheduled':
console.log(`Đặt lịch được lên lịch lại: ${data.event.start_time}`)
// Cập nhật lịch, thông báo nhóm...
updateCRM(data)
break
}
}
Kiểm thử với Apidog
API Calendly yêu cầu OAuth, khiến việc kiểm thử thủ công phức tạp. Apidog giúp đơn giản hóa quy trình này.
1. Mô phỏng phản hồi OAuth
Khi phát triển, bạn có thể mô phỏng phản hồi token thay vì chạy toàn bộ luồng OAuth:
{
"access_token": "mock_access_token",
"refresh_token": "mock_refresh_token",
"expires_in": 7200,
"created_at": 1700000000
}
2. Kiểm thử trình xử lý webhook
Tạo payload webhook giả lập:
{
"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"
}
}
}
}
Gửi đến endpoint webhook và xác minh logic xử lý.
3. Biến môi trường
Thiết lập biến môi trường cho quy trình OAuth & webhook:
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. Xác thực chữ ký webhook
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')
// Xác minh chữ ký
const valid = verifySignature(payload, signature, secret)
pm.expect(valid).to.be.true
})
Kiểm thử webhook Calendly với Apidog hoàn toàn miễn phí.
Các lỗi phổ biến và cách khắc phục
401 Không được ủy quyền
Nguyên nhân: Token không hợp lệ hoặc hết hạn.
Khắc phục:
- Kiểm tra token còn hạn (token hết hạn sau 2 giờ)
- Sử dụng refresh token lấy access token mới
- Đảm bảo header Authorization là
Bearer {token}
403 Bị cấm
Nguyên nhân: Không đủ phạm vi OAuth.
Khắc phục:
- Đảm bảo access token có đủ scope
- Khi yêu cầu ủy quyền, bao gồm scope cần thiết
- Scope của Calendly dựa vào những gì user cấp
404 Không tìm thấy
Nguyên nhân: Tài nguyên không tồn tại hoặc không có quyền truy cập.
Khắc phục:
- Xác minh URI tài nguyên chính xác
- Đảm bảo user xác thực có quyền truy cập tài nguyên
- Kiểm tra ID sự kiện/loại sự kiện hợp lệ
422 Thực thể không thể xử lý
Nguyên nhân: Lỗi xác thực trong request.
Khắc phục: Xem chi tiết trong phản hồi:
{
"title": "Lỗi xác thực",
"message": "Tham số không hợp lệ: url phải là một URL HTTPS hợp lệ"
}
Các lựa chọn thay thế và so sánh
| Tính năng | Calendly | Acuity | Cal.com | Calendly |
|---|---|---|---|---|
| Gói miễn phí | Hạn chế | Hạn chế | Miễn phí tự lưu trữ | ✓ |
| Truy cập API | ✓ | ✓ | ✓ | ✓ |
| Webhooks | ✓ | ✓ | ✓ | ✓ |
| OAuth | ✓ | Khóa API | Khóa API | OAuth |
| Lên lịch nhóm | ✓ | ✓ | ✓ | ✓ |
| Mã nguồn mở | Không | Không | Có | Không |
Calendly có API docs và luồng OAuth hoàn thiện nhất. Cal.com là lựa chọn mã nguồn mở, xác thực bằng API key đơn giản hơn.
Các trường hợp sử dụng thực tế
Tích hợp CRM bán hàng:
Một công ty SaaS B2B nhúng Calendly vào trang giá. Khi có booking demo, webhook kích hoạt:
- Tạo lead trong Salesforce
- Gửi Slack thông báo cho sales team
- Thêm vào automation marketing
- Ghi lại hoạt động trong hệ thống chăm sóc khách hàng
Nền tảng tư vấn:
Khách hàng đặt lịch tư vấn với luật sư. API tích hợp:
- Đồng bộ lịch với hệ thống nội bộ
- Tạo link Zoom
- Gửi survey 24 giờ trước
- Tạo hồ sơ vụ việc sau khi họp
Lên lịch phỏng vấn:
Nền tảng tuyển dụng tích hợp webhook Calendly:
- Cập nhật ATS với chi tiết phỏng vấn
- Email thông báo cho manager
- Gửi calendar invite cho tất cả
- Theo dõi các trường hợp vắng mặt
Kết luận
Bạn đã biết cách:
- Xác thực API Calendly bằng OAuth 2.0
- Truy cập loại sự kiện, sự kiện đã đặt lịch qua API
- Dùng webhook nhận thông báo thời gian thực
- Xác minh chữ ký webhook đảm bảo bảo mật
- Kiểm thử toàn bộ quy trình với Apidog trước khi kết nối lịch thật
Các bước tiếp theo:
- Tạo ứng dụng OAuth trong Calendly
- Triển khai luồng OAuth
- Thiết lập đăng ký webhook
- Kiểm thử với payload giả lập trong Apidog
- Đưa vào môi trường production
Kiểm thử webhook Calendly với Apidog - miễn phí.
Câu hỏi thường gặp
Tôi có cần gói trả phí của Calendly để dùng API không?
Không. API có trên mọi gói kể cả miễn phí, nhưng tính năng sẽ hạn chế. Webhook cũng có ở mọi gói.
Webhook cấp user và cấp tổ chức khác gì nhau?
Webhook user chỉ ghi nhận sự kiện của một user. Webhook tổ chức ghi nhận sự kiện cho cả nhóm. Hầu hết tích hợp nên dùng scope tổ chức.
Làm sao lấy secret ký webhook?
Khi tạo webhook qua API, response trả về signing_key. Lưu trữ an toàn để xác minh chữ ký webhook.
Có thể tạo đặt lịch qua API không?
Không. Calendly chưa hỗ trợ endpoint tạo lịch qua API, chỉ có quyền đọc các booking.
Xử lý chuyển đổi múi giờ như thế nào?
Tất cả timestamp API trả về là UTC (ISO 8601). Chuyển về local trong ứng dụng. Múi giờ user có trong resource user.
Giới hạn tốc độ là gì?
Calendly không công bố rate limit. Hãy thực hiện request hợp lý, nếu gặp limit thì dùng exponential backoff.
Có thể lấy các lịch trong quá khứ không?
Có. Dùng min_start_time, max_start_time để truy vấn lịch sử, không giới hạn thời gian.
Kiểm thử luồng OAuth cục bộ thế nào?
Dùng dịch vụ tunneling (ngrok) để expose server local. Đặt redirect URI là URL ngrok, hoàn thành OAuth trên browser và kiểm tra callback.

Top comments (0)