# Authentication API Reference

Detailed documentation for authentication methods in the Cal.diy API v2.

## Authentication Methods

Cal.diy 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.diy 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.diy 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.diy 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
