# Authentication API Reference Detailed documentation for authentication methods in the Cal.com API v2. ## Authentication Methods Cal.com API v2 supports two authentication methods: 1. **API Key Authentication** - For direct API access 2. **OAuth/Platform Authentication** - For platform integrations managing users on behalf of others ## API Key Authentication The primary authentication method for most API consumers. ### Obtaining an API Key 1. Log in to your Cal.com account 2. Navigate to Settings > Developer > API Keys 3. Click "Create new API key" 4. Copy and securely store the generated key ### Using API Keys Include the API key in the `Authorization` header with the `Bearer` prefix: ```http GET /v2/bookings Authorization: Bearer cal_live_abc123xyz... ``` ### API Key Format All Cal.com API keys are prefixed with `cal_`: - `cal_live_...` - Production API keys - `cal_test_...` - Test/sandbox API keys (if available) ### Example Request ```bash curl -X GET "https://api.cal.com/v2/bookings" \ -H "Authorization: Bearer cal_live_abc123xyz789" \ -H "Content-Type: application/json" ``` ## Refresh API Key Generate a new API key and invalidate the current one: ```http POST /v2/api-keys/refresh Authorization: Bearer cal_live_current_key Content-Type: application/json { "expiresAt": "2025-12-31T23:59:59Z" } ``` ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | expiresAt | string | No | ISO 8601 expiration date for the new key | ### Response ```json { "status": "success", "data": { "apiKey": "cal_live_new_key_xyz..." } } ``` ## Platform Authentication (OAuth) For platform customers building integrations that manage multiple users. ### Headers for Platform Authentication 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) | ### Example Platform Request ```bash 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" ``` ### When to Use Each Header **For endpoints acting on behalf of a managed user:** ```http 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):** ```http GET /v2/oauth-clients Authorization: Bearer cal_live_platform_admin_key ``` ## API Versioning Many endpoints require a version header: ```http cal-api-version: 2024-08-13 ``` ### Example with Version Header ```bash 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, ...}' ``` ## Authentication Errors ### 401 Unauthorized Returned when authentication fails: ```json { "status": "error", "error": { "code": "UNAUTHORIZED", "message": "Invalid API key" } } ``` Common causes: - Missing `Authorization` header - Invalid or expired API key - API key without `cal_` prefix - Incorrect Bearer token format ### 403 Forbidden Returned when authenticated but lacking permissions: ```json { "status": "error", "error": { "code": "FORBIDDEN", "message": "You do not have permission to access this resource" } } ``` Common causes: - Accessing another user's resources - Missing required scopes for platform tokens - Organization/team permission restrictions ## Security Best Practices 1. **Never expose API keys in client-side code**: API keys should only be used in server-side applications 2. **Use environment variables**: Store API keys in environment variables, not in code ```bash export CAL_API_KEY="cal_live_abc123..." ``` 3. **Rotate keys regularly**: Use the refresh endpoint to rotate keys periodically 4. **Use minimal permissions**: Request only the scopes/permissions your application needs 5. **Monitor API usage**: Check your Cal.com dashboard for unusual activity 6. **Secure transmission**: Always use HTTPS for API requests 7. **Handle keys securely in logs**: Never log full API keys - redact sensitive portions ## Rate Limiting API requests are rate limited. When exceeded, you'll receive: ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 ``` ```json { "status": "error", "error": { "code": "RATE_LIMITED", "message": "Too many requests. Please retry after 60 seconds." } } ``` ### Rate Limit Headers | 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) | ## Testing Authentication Verify your API key is working: ```bash curl -X GET "https://api.cal.com/v2/me" \ -H "Authorization: Bearer cal_live_your_api_key" \ -H "Content-Type: application/json" ``` ### Expected Response ```json { "status": "success", "data": { "id": 12345, "email": "user@example.com", "username": "johndoe", "name": "John Doe", "timeZone": "America/New_York" } } ``` ## Common Authentication Patterns ### Server-Side Integration ```javascript 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(); } ``` ### Handling Token Refresh ```javascript 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; } ``` ## Error Handling 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 | ### Error Response Format ```json { "status": "error", "error": { "code": "ERROR_CODE", "message": "Human-readable error message" } } ``` ### Common Error Codes | 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 | ## Pagination 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 | ### Example ```http GET /v2/bookings?take=20&skip=40 ``` This returns items 41-60 (skipping the first 40, taking 20). ### Pagination Response Some endpoints include pagination metadata: ```json { "status": "success", "data": [...], "pagination": { "total": 150, "take": 20, "skip": 40 } } ``` ## Troubleshooting ### "Invalid API key" Error 1. Verify the key starts with `cal_` 2. Check for extra whitespace or characters 3. Ensure the key hasn't been revoked or expired 4. Confirm you're using the correct environment (production vs test) ### "Missing Authorization header" Error 1. Ensure the header name is exactly `Authorization` 2. Include the `Bearer ` prefix (with space) 3. Check for typos in the header name ### Platform Authentication Issues 1. Verify both `x-cal-client-id` and `x-cal-secret-key` are provided 2. Ensure the managed user access token is valid 3. Check that the OAuth client has the required permissions