agents/skills/calcom-api/references/authentication.md
Detailed documentation for authentication methods in the Cal.com API v2.
Cal.com API v2 supports two authentication methods:
The primary authentication method for most API consumers.
Include the API key in the Authorization header with the Bearer prefix:
GET /v2/bookings
Authorization: Bearer cal_live_abc123xyz...
All Cal.com API keys are prefixed with cal_:
cal_live_... - Production API keyscal_test_... - Test/sandbox API keys (if available)curl -X GET "https://api.cal.com/v2/bookings" \
-H "Authorization: Bearer cal_live_abc123xyz789" \
-H "Content-Type: application/json"
Generate a new API key and invalidate the current one:
POST /v2/api-keys/refresh
Authorization: Bearer cal_live_current_key
Content-Type: application/json
{
"expiresAt": "2025-12-31T23:59:59Z"
}
| Field | Type | Required | Description |
|---|---|---|---|
| expiresAt | string | No | ISO 8601 expiration date for the new key |
{
"status": "success",
"data": {
"apiKey": "cal_live_new_key_xyz..."
}
}
For platform customers building integrations that manage multiple users.
Platform customers use additional headers alongside or instead of the Bearer token:
| Header | Description |
|---|---|
x-cal-client-id | OAuth client ID |
x-cal-secret-key | OAuth client secret key |
Authorization | Bearer token (managed user access token) |
curl -X GET "https://api.cal.com/v2/bookings" \
-H "x-cal-client-id: your_client_id" \
-H "x-cal-secret-key: your_secret_key" \
-H "Authorization: Bearer managed_user_access_token" \
-H "Content-Type: application/json"
For endpoints acting on behalf of a managed user:
GET /v2/bookings
x-cal-client-id: your_client_id
x-cal-secret-key: your_secret_key
Authorization: Bearer managed_user_access_token
For platform-level operations (managing OAuth clients):
GET /v2/oauth-clients
Authorization: Bearer cal_live_platform_admin_key
Many endpoints require a version header:
cal-api-version: 2024-08-13
curl -X POST "https://api.cal.com/v2/bookings" \
-H "Authorization: Bearer cal_live_abc123" \
-H "cal-api-version: 2024-08-13" \
-H "Content-Type: application/json" \
-d '{"start": "2024-01-15T10:00:00Z", "eventTypeId": 123, ...}'
Returned when authentication fails:
{
"status": "error",
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
}
}
Common causes:
Authorization headercal_ prefixReturned when authenticated but lacking permissions:
{
"status": "error",
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to access this resource"
}
}
Common causes:
Never expose API keys in client-side code: API keys should only be used in server-side applications
Use environment variables: Store API keys in environment variables, not in code
export CAL_API_KEY="cal_live_abc123..."
Rotate keys regularly: Use the refresh endpoint to rotate keys periodically
Use minimal permissions: Request only the scopes/permissions your application needs
Monitor API usage: Check your Cal.com dashboard for unusual activity
Secure transmission: Always use HTTPS for API requests
Handle keys securely in logs: Never log full API keys - redact sensitive portions
API requests are rate limited. When exceeded, you'll receive:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"status": "error",
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Please retry after 60 seconds."
}
}
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait before retrying (on 429) |
Verify your API key is working:
curl -X GET "https://api.cal.com/v2/me" \
-H "Authorization: Bearer cal_live_your_api_key" \
-H "Content-Type: application/json"
{
"status": "success",
"data": {
"id": 12345,
"email": "[email protected]",
"username": "johndoe",
"name": "John Doe",
"timeZone": "America/New_York"
}
}
const CAL_API_KEY = process.env.CAL_API_KEY;
async function getBookings() {
const response = await fetch('https://api.cal.com/v2/bookings', {
headers: {
'Authorization': `Bearer ${CAL_API_KEY}`,
'Content-Type': 'application/json',
'cal-api-version': '2024-08-13'
}
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('Invalid API key');
}
throw new Error(`API error: ${response.status}`);
}
return response.json();
}
async function makeAuthenticatedRequest(url, options = {}) {
let response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${apiKey}`
}
});
if (response.status === 401) {
// Refresh the API key
const refreshResponse = await fetch('https://api.cal.com/v2/api-keys/refresh', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
if (refreshResponse.ok) {
const { data } = await refreshResponse.json();
apiKey = data.apiKey;
// Retry original request with new key
response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${apiKey}`
}
});
}
}
return response;
}
The API returns standard HTTP status codes:
| Status Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request (invalid parameters) |
| 401 | Unauthorized (invalid or missing API key) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found |
| 422 | Unprocessable Entity (validation error) |
| 429 | Too Many Requests (rate limited) |
| 500 | Internal Server Error |
{
"status": "error",
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message"
}
}
| Code | Description |
|---|---|
| UNAUTHORIZED | Invalid or missing authentication |
| FORBIDDEN | Insufficient permissions |
| NOT_FOUND | Resource not found |
| VALIDATION_ERROR | Invalid request parameters |
| RATE_LIMITED | Too many requests |
List endpoints support pagination via take and skip parameters:
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
| take | number | 10 | 250 | Number of items to return |
| skip | number | 0 | - | Number of items to skip |
GET /v2/bookings?take=20&skip=40
This returns items 41-60 (skipping the first 40, taking 20).
Some endpoints include pagination metadata:
{
"status": "success",
"data": [...],
"pagination": {
"total": 150,
"take": 20,
"skip": 40
}
}
cal_AuthorizationBearer prefix (with space)x-cal-client-id and x-cal-secret-key are provided