docs: declare credentials and environment variables in calcom-api skill (#28047)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
c321a6c07a
commit
66ce20245c
@@ -1,6 +1,19 @@
|
||||
---
|
||||
name: calcom-api
|
||||
description: Interact with the Cal.com API v2 to manage scheduling, bookings, event types, availability, and calendars. Use this skill when building integrations that need to create or manage bookings, check availability, configure event types, or sync calendars with Cal.com's scheduling infrastructure.
|
||||
env:
|
||||
CAL_API_KEY:
|
||||
description: "Cal.com API key (prefixed with cal_live_ or cal_test_). Required for all API requests."
|
||||
required: true
|
||||
CAL_CLIENT_ID:
|
||||
description: "OAuth client ID for platform integrations managing users on behalf of others. Sent as x-cal-client-id header."
|
||||
required: false
|
||||
CAL_SECRET_KEY:
|
||||
description: "OAuth client secret for platform integrations. Sent as x-cal-secret-key header."
|
||||
required: false
|
||||
CAL_WEBHOOK_SECRET:
|
||||
description: "Secret used to verify webhook payload signatures via X-Cal-Signature-256 header."
|
||||
required: false
|
||||
---
|
||||
|
||||
# Cal.com API v2
|
||||
@@ -14,6 +27,15 @@ All API requests should be made to:
|
||||
https://api.cal.com/v2
|
||||
```
|
||||
|
||||
## Required Credentials
|
||||
|
||||
| Environment Variable | Required | Description |
|
||||
|---------------------|----------|-------------|
|
||||
| `CAL_API_KEY` | Yes | Cal.com API key (prefixed with `cal_live_` or `cal_test_`). Used as Bearer token for all API requests. Generate from Settings > Developer > API Keys. |
|
||||
| `CAL_CLIENT_ID` | No | OAuth client ID for platform integrations that manage users on behalf of others. Sent as `x-cal-client-id` header. |
|
||||
| `CAL_SECRET_KEY` | No | OAuth client secret for platform integrations. Sent as `x-cal-secret-key` header. |
|
||||
| `CAL_WEBHOOK_SECRET` | No | Secret for verifying webhook payload signatures via the `X-Cal-Signature-256` header. |
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests require authentication via Bearer token:
|
||||
|
||||
@@ -1,321 +0,0 @@
|
||||
---
|
||||
name: calcom-api
|
||||
description: Interact with the Cal.com API v2 to manage scheduling, bookings, event types, availability, and calendars. Use this skill when building integrations that need to create or manage bookings, check availability, configure event types, or sync calendars with Cal.com's scheduling infrastructure.
|
||||
license: MIT
|
||||
metadata:
|
||||
author: calcom
|
||||
version: "1.0.0"
|
||||
api-version: "v2"
|
||||
---
|
||||
|
||||
# Cal.com API v2
|
||||
|
||||
This skill provides guidance for AI agents to interact with the Cal.com API v2, enabling scheduling automation, booking management, and calendar integrations.
|
||||
|
||||
## Base URL
|
||||
|
||||
All API requests should be made to:
|
||||
```
|
||||
https://api.cal.com/v2
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
All API requests require authentication via Bearer token in the Authorization header:
|
||||
|
||||
```
|
||||
Authorization: Bearer cal_<your_api_key>
|
||||
```
|
||||
|
||||
API keys must be prefixed with `cal_`. You can generate API keys from your Cal.com dashboard under Settings > Developer > API Keys.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Event Types
|
||||
Event types define bookable meeting configurations (duration, location, availability rules). Each event type has a unique slug used in booking URLs.
|
||||
|
||||
### Bookings
|
||||
Bookings are confirmed appointments created when someone books an event type. Each booking has a unique UID for identification.
|
||||
|
||||
### Schedules
|
||||
Schedules define when a user is available for bookings. Users can have multiple schedules with different working hours.
|
||||
|
||||
### Slots
|
||||
Slots represent available time windows that can be booked based on event type configuration and user availability.
|
||||
|
||||
## Common Operations
|
||||
|
||||
### Check Available Slots
|
||||
|
||||
Before creating a booking, check available time slots:
|
||||
|
||||
```http
|
||||
GET /v2/slots?startTime=2024-01-15T00:00:00Z&endTime=2024-01-22T00:00:00Z&eventTypeId=123&eventTypeSlug=30min
|
||||
```
|
||||
|
||||
Query parameters:
|
||||
- `startTime` (required): ISO 8601 start of range
|
||||
- `endTime` (required): ISO 8601 end of range
|
||||
- `eventTypeId` or `eventTypeSlug`: Identify the event type
|
||||
- `timeZone`: Timezone for slot display (default: UTC)
|
||||
|
||||
Response contains available slots grouped by date.
|
||||
|
||||
### Create a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"start": "2024-01-15T10:00:00Z",
|
||||
"eventTypeId": 123,
|
||||
"attendee": {
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"timeZone": "America/New_York"
|
||||
},
|
||||
"meetingUrl": "https://cal.com/team/meeting",
|
||||
"metadata": {}
|
||||
}
|
||||
```
|
||||
|
||||
Required fields:
|
||||
- `start`: ISO 8601 booking start time
|
||||
- `eventTypeId`: ID of the event type to book
|
||||
- `attendee.name`: Attendee's full name
|
||||
- `attendee.email`: Attendee's email address
|
||||
- `attendee.timeZone`: Attendee's timezone
|
||||
|
||||
### Get Bookings
|
||||
|
||||
List bookings with optional filters:
|
||||
|
||||
```http
|
||||
GET /v2/bookings?status=upcoming&take=10
|
||||
```
|
||||
|
||||
Query parameters:
|
||||
- `status`: Filter by status (upcoming, recurring, past, cancelled, unconfirmed)
|
||||
- `attendeeEmail`: Filter by attendee email
|
||||
- `eventTypeId`: Filter by event type
|
||||
- `take`: Number of results (max 250)
|
||||
- `skip`: Pagination offset
|
||||
|
||||
### Get a Single Booking
|
||||
|
||||
```http
|
||||
GET /v2/bookings/{bookingUid}
|
||||
```
|
||||
|
||||
### Cancel a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/cancel
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"cancellationReason": "Schedule conflict"
|
||||
}
|
||||
```
|
||||
|
||||
### Reschedule a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/reschedule
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"start": "2024-01-16T14:00:00Z",
|
||||
"reschedulingReason": "Conflict with another meeting"
|
||||
}
|
||||
```
|
||||
|
||||
### List Event Types
|
||||
|
||||
```http
|
||||
GET /v2/event-types
|
||||
```
|
||||
|
||||
Returns all event types for the authenticated user.
|
||||
|
||||
### Get a Single Event Type
|
||||
|
||||
```http
|
||||
GET /v2/event-types/{eventTypeId}
|
||||
```
|
||||
|
||||
### Create an Event Type
|
||||
|
||||
```http
|
||||
POST /v2/event-types
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "30 Minute Meeting",
|
||||
"slug": "30min",
|
||||
"lengthInMinutes": 30,
|
||||
"locations": [
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "cal-video"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### List Schedules
|
||||
|
||||
```http
|
||||
GET /v2/schedules
|
||||
```
|
||||
|
||||
### Get Default Schedule
|
||||
|
||||
```http
|
||||
GET /v2/schedules/default
|
||||
```
|
||||
|
||||
### Create a Schedule
|
||||
|
||||
```http
|
||||
POST /v2/schedules
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Working Hours",
|
||||
"timeZone": "America/New_York",
|
||||
"isDefault": true,
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "09:00",
|
||||
"endTime": "17:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Days are 0-indexed (0 = Sunday, 1 = Monday, etc.).
|
||||
|
||||
### Get Current User
|
||||
|
||||
```http
|
||||
GET /v2/me
|
||||
```
|
||||
|
||||
Returns the authenticated user's profile information.
|
||||
|
||||
## Team and Organization Endpoints
|
||||
|
||||
For team bookings and organization management, use the organization-scoped endpoints:
|
||||
|
||||
### List Organization Teams
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/teams
|
||||
```
|
||||
|
||||
### Get Team Event Types
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/teams/{teamId}/event-types
|
||||
```
|
||||
|
||||
### Create Team Booking
|
||||
|
||||
Team event types support different scheduling modes:
|
||||
- `COLLECTIVE`: All team members must attend
|
||||
- `ROUND_ROBIN`: Distributes bookings among team members
|
||||
|
||||
## Webhooks
|
||||
|
||||
Configure webhooks to receive real-time notifications:
|
||||
|
||||
### List Webhooks
|
||||
|
||||
```http
|
||||
GET /v2/webhooks
|
||||
```
|
||||
|
||||
### Create a Webhook
|
||||
|
||||
```http
|
||||
POST /v2/webhooks
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"subscriberUrl": "https://your-app.com/webhook",
|
||||
"triggers": ["BOOKING_CREATED", "BOOKING_CANCELLED"],
|
||||
"active": true
|
||||
}
|
||||
```
|
||||
|
||||
Available triggers:
|
||||
- `BOOKING_CREATED`
|
||||
- `BOOKING_CANCELLED`
|
||||
- `BOOKING_RESCHEDULED`
|
||||
- `BOOKING_CONFIRMED`
|
||||
- `MEETING_STARTED`
|
||||
- `MEETING_ENDED`
|
||||
|
||||
## Calendar Integration
|
||||
|
||||
### List Connected Calendars
|
||||
|
||||
```http
|
||||
GET /v2/calendars
|
||||
```
|
||||
|
||||
### Check Busy Times
|
||||
|
||||
```http
|
||||
GET /v2/calendars/busy-times?startTime=2024-01-15T00:00:00Z&endTime=2024-01-22T00:00:00Z
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The API returns standard HTTP status codes:
|
||||
|
||||
- `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)
|
||||
- `500`: Internal Server Error
|
||||
|
||||
Error responses include a message field:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Booking not found"
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
The API implements rate limiting. If you exceed the limit, you'll receive a `429 Too Many Requests` response. Implement exponential backoff for retries.
|
||||
|
||||
## Pagination
|
||||
|
||||
List endpoints support pagination via `take` and `skip` parameters:
|
||||
|
||||
- `take`: Number of items to return (default: 10, max: 250)
|
||||
- `skip`: Number of items to skip
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Always check slot availability before creating bookings
|
||||
2. Store booking UIDs for future reference (cancel, reschedule)
|
||||
3. Handle timezone conversions carefully - always use ISO 8601 format
|
||||
4. Implement webhook handlers for real-time booking updates
|
||||
5. Cache event type data to reduce API calls
|
||||
6. Use appropriate error handling for all API calls
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Full API Reference](https://cal.com/docs/api-reference/v2)
|
||||
- [OpenAPI Specification](https://api.cal.com/v2/docs)
|
||||
@@ -1,335 +0,0 @@
|
||||
# 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;
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
@@ -1,323 +0,0 @@
|
||||
# Bookings API Reference
|
||||
|
||||
Detailed documentation for booking-related endpoints in the Cal.com API v2.
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /v2/bookings | List bookings |
|
||||
| POST | /v2/bookings | Create a booking |
|
||||
| GET | /v2/bookings/{bookingUid} | Get a booking |
|
||||
| POST | /v2/bookings/{bookingUid}/cancel | Cancel a booking |
|
||||
| POST | /v2/bookings/{bookingUid}/reschedule | Reschedule a booking |
|
||||
| POST | /v2/bookings/{bookingUid}/confirm | Confirm a pending booking |
|
||||
| POST | /v2/bookings/{bookingUid}/decline | Decline a booking |
|
||||
| PATCH | /v2/bookings/{bookingUid}/location | Update booking location |
|
||||
| POST | /v2/bookings/{bookingUid}/mark-absent | Mark attendee as no-show |
|
||||
| POST | /v2/bookings/{bookingUid}/reassign | Reassign booking to another host |
|
||||
| GET | /v2/bookings/{bookingUid}/references | Get booking references |
|
||||
|
||||
## List Bookings
|
||||
|
||||
```http
|
||||
GET /v2/bookings
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| status | string | No | Filter by status: `upcoming`, `recurring`, `past`, `cancelled`, `unconfirmed` |
|
||||
| attendeeEmail | string | No | Filter by attendee email |
|
||||
| attendeeName | string | No | Filter by attendee name |
|
||||
| eventTypeId | number | No | Filter by event type ID |
|
||||
| eventTypeIds | string | No | Comma-separated event type IDs |
|
||||
| teamsIds | string | No | Comma-separated team IDs |
|
||||
| afterStart | string | No | Filter bookings starting after this ISO 8601 date |
|
||||
| beforeEnd | string | No | Filter bookings ending before this ISO 8601 date |
|
||||
| sortStart | string | No | Sort by start time: `asc` or `desc` |
|
||||
| sortEnd | string | No | Sort by end time: `asc` or `desc` |
|
||||
| sortCreated | string | No | Sort by creation time: `asc` or `desc` |
|
||||
| take | number | No | Number of results (default: 10, max: 250) |
|
||||
| skip | number | No | Pagination offset |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 12345,
|
||||
"uid": "abc123def456",
|
||||
"title": "30 Minute Meeting",
|
||||
"description": "Discussion about project",
|
||||
"start": "2024-01-15T10:00:00.000Z",
|
||||
"end": "2024-01-15T10:30:00.000Z",
|
||||
"status": "accepted",
|
||||
"eventTypeId": 123,
|
||||
"attendees": [
|
||||
{
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"timeZone": "America/New_York"
|
||||
}
|
||||
],
|
||||
"hosts": [
|
||||
{
|
||||
"id": 456,
|
||||
"name": "Jane Smith",
|
||||
"email": "jane@company.com"
|
||||
}
|
||||
],
|
||||
"location": "https://cal.com/video/abc123",
|
||||
"meetingUrl": "https://cal.com/video/abc123",
|
||||
"metadata": {},
|
||||
"createdAt": "2024-01-10T08:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Create a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"start": "2024-01-15T10:00:00Z",
|
||||
"eventTypeId": 123,
|
||||
"attendee": {
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"timeZone": "America/New_York",
|
||||
"language": "en"
|
||||
},
|
||||
"guests": ["guest1@example.com", "guest2@example.com"],
|
||||
"meetingUrl": "https://cal.com/team/meeting",
|
||||
"metadata": {
|
||||
"customField": "value"
|
||||
},
|
||||
"bookingFieldsResponses": {
|
||||
"notes": "Please prepare the quarterly report"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Required Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| start | string | ISO 8601 booking start time |
|
||||
| eventTypeId | number | ID of the event type to book |
|
||||
| attendee.name | string | Attendee's full name |
|
||||
| attendee.email | string | Attendee's email address |
|
||||
| attendee.timeZone | string | Attendee's timezone (IANA format) |
|
||||
|
||||
### Optional Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| attendee.language | string | Attendee's preferred language |
|
||||
| guests | array | Additional guest email addresses |
|
||||
| meetingUrl | string | Custom meeting URL |
|
||||
| metadata | object | Custom metadata |
|
||||
| bookingFieldsResponses | object | Responses to custom booking fields |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"id": 12345,
|
||||
"uid": "abc123def456",
|
||||
"title": "30 Minute Meeting",
|
||||
"start": "2024-01-15T10:00:00.000Z",
|
||||
"end": "2024-01-15T10:30:00.000Z",
|
||||
"status": "accepted",
|
||||
"eventTypeId": 123,
|
||||
"attendees": [...],
|
||||
"hosts": [...],
|
||||
"location": "https://cal.com/video/abc123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Get a Booking
|
||||
|
||||
```http
|
||||
GET /v2/bookings/{bookingUid}
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| bookingUid | string | Unique booking identifier |
|
||||
|
||||
### Response
|
||||
|
||||
Returns the full booking object with all details.
|
||||
|
||||
## Cancel a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/cancel
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"cancellationReason": "Schedule conflict"
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| cancellationReason | string | No | Reason for cancellation |
|
||||
|
||||
## Reschedule a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/reschedule
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"start": "2024-01-16T14:00:00Z",
|
||||
"reschedulingReason": "Conflict with another meeting"
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| start | string | Yes | New booking start time (ISO 8601) |
|
||||
| reschedulingReason | string | No | Reason for rescheduling |
|
||||
|
||||
## Confirm a Booking
|
||||
|
||||
For event types that require confirmation:
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/confirm
|
||||
```
|
||||
|
||||
## Decline a Booking
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/decline
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"reason": "Not available at this time"
|
||||
}
|
||||
```
|
||||
|
||||
## Update Booking Location
|
||||
|
||||
```http
|
||||
PATCH /v2/bookings/{bookingUid}/location
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"location": "https://zoom.us/j/123456789"
|
||||
}
|
||||
```
|
||||
|
||||
## Mark Attendee as No-Show
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/mark-absent
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"attendeeEmail": "john@example.com",
|
||||
"noShow": true
|
||||
}
|
||||
```
|
||||
|
||||
## Reassign Booking
|
||||
|
||||
Reassign a booking to a different host:
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/reassign
|
||||
```
|
||||
|
||||
Or to a specific user:
|
||||
|
||||
```http
|
||||
POST /v2/bookings/{bookingUid}/reassign/{userId}
|
||||
```
|
||||
|
||||
## Get Booking References
|
||||
|
||||
Get external references (calendar events, video meetings) for a booking:
|
||||
|
||||
```http
|
||||
GET /v2/bookings/{bookingUid}/references
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"type": "google_calendar",
|
||||
"uid": "calendar-event-id",
|
||||
"meetingUrl": "https://meet.google.com/abc-defg-hij"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Booking Statuses
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| accepted | Booking is confirmed |
|
||||
| pending | Awaiting confirmation |
|
||||
| cancelled | Booking was cancelled |
|
||||
| rejected | Booking was declined |
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Book a Meeting
|
||||
|
||||
1. Get available slots: `GET /v2/slots?eventTypeId=123&startTime=...&endTime=...`
|
||||
2. Create booking: `POST /v2/bookings` with selected slot
|
||||
3. Store the booking UID for future operations
|
||||
|
||||
### Reschedule Flow
|
||||
|
||||
1. Get new available slots: `GET /v2/slots?eventTypeId=123&startTime=...&endTime=...`
|
||||
2. Reschedule: `POST /v2/bookings/{uid}/reschedule` with new start time
|
||||
|
||||
### Cancel with Notification
|
||||
|
||||
1. Cancel: `POST /v2/bookings/{uid}/cancel` with reason
|
||||
2. Attendees automatically receive cancellation emails
|
||||
@@ -1,327 +0,0 @@
|
||||
# Calendars API Reference
|
||||
|
||||
Detailed documentation for calendar integration endpoints in the Cal.com API v2.
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /v2/calendars | List connected calendars |
|
||||
| GET | /v2/calendars/busy-times | Get busy times |
|
||||
| GET | /v2/calendars/{calendar}/check | Check calendar connection |
|
||||
| POST | /v2/calendars/{calendar}/connect | Connect a calendar |
|
||||
| DELETE | /v2/calendars/{calendar}/disconnect | Disconnect a calendar |
|
||||
| GET | /v2/calendars/{calendar}/credentials | Get calendar credentials |
|
||||
| GET | /v2/destination-calendars | List destination calendars |
|
||||
| GET | /v2/selected-calendars | List selected calendars |
|
||||
|
||||
## List Connected Calendars
|
||||
|
||||
```http
|
||||
GET /v2/calendars
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"connectedCalendars": [
|
||||
{
|
||||
"integration": {
|
||||
"type": "google_calendar",
|
||||
"title": "Google Calendar",
|
||||
"slug": "google-calendar"
|
||||
},
|
||||
"credentialId": 123,
|
||||
"primary": {
|
||||
"externalId": "primary",
|
||||
"name": "john@gmail.com",
|
||||
"email": "john@gmail.com",
|
||||
"isSelected": true,
|
||||
"readOnly": false
|
||||
},
|
||||
"calendars": [
|
||||
{
|
||||
"externalId": "primary",
|
||||
"name": "john@gmail.com",
|
||||
"email": "john@gmail.com",
|
||||
"isSelected": true,
|
||||
"readOnly": false
|
||||
},
|
||||
{
|
||||
"externalId": "calendar-id-2",
|
||||
"name": "Work Calendar",
|
||||
"email": "john@gmail.com",
|
||||
"isSelected": true,
|
||||
"readOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"destinationCalendar": {
|
||||
"id": 1,
|
||||
"integration": "google_calendar",
|
||||
"externalId": "primary",
|
||||
"name": "john@gmail.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Get Busy Times
|
||||
|
||||
Check busy times from connected calendars to understand availability.
|
||||
|
||||
```http
|
||||
GET /v2/calendars/busy-times
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| startTime | string | Yes | ISO 8601 start of date range |
|
||||
| endTime | string | Yes | ISO 8601 end of date range |
|
||||
| loggedInUsersTz | string | No | User's timezone |
|
||||
| credentialId | number | No | Specific calendar credential ID |
|
||||
|
||||
### Example Request
|
||||
|
||||
```http
|
||||
GET /v2/calendars/busy-times?startTime=2024-01-15T00:00:00Z&endTime=2024-01-22T00:00:00Z&loggedInUsersTz=America/New_York
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"start": "2024-01-15T10:00:00.000Z",
|
||||
"end": "2024-01-15T11:00:00.000Z",
|
||||
"title": "Team Meeting",
|
||||
"source": "google_calendar"
|
||||
},
|
||||
{
|
||||
"start": "2024-01-16T14:00:00.000Z",
|
||||
"end": "2024-01-16T15:00:00.000Z",
|
||||
"title": "Client Call",
|
||||
"source": "google_calendar"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Calendar Connection
|
||||
|
||||
### Check Calendar Connection
|
||||
|
||||
```http
|
||||
GET /v2/calendars/{calendar}/check
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| calendar | string | Calendar type (e.g., `google-calendar`, `office365-calendar`) |
|
||||
|
||||
### Connect a Calendar
|
||||
|
||||
```http
|
||||
POST /v2/calendars/{calendar}/connect
|
||||
```
|
||||
|
||||
This initiates the OAuth flow for calendar connection.
|
||||
|
||||
### Disconnect a Calendar
|
||||
|
||||
```http
|
||||
DELETE /v2/calendars/{calendar}/disconnect
|
||||
```
|
||||
|
||||
## Supported Calendar Types
|
||||
|
||||
| Type | Slug | Description |
|
||||
|------|------|-------------|
|
||||
| Google Calendar | google-calendar | Google Workspace calendars |
|
||||
| Microsoft 365 | office365-calendar | Outlook/Microsoft 365 calendars |
|
||||
| Apple Calendar | apple-calendar | iCloud calendars (CalDAV) |
|
||||
| CalDAV | caldav-calendar | Generic CalDAV calendars |
|
||||
|
||||
## Destination Calendars
|
||||
|
||||
The destination calendar is where new bookings are created.
|
||||
|
||||
### List Destination Calendars
|
||||
|
||||
```http
|
||||
GET /v2/destination-calendars
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"integration": "google_calendar",
|
||||
"externalId": "primary",
|
||||
"name": "john@gmail.com",
|
||||
"userId": 123,
|
||||
"eventTypeId": null,
|
||||
"credentialId": 456
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Selected Calendars
|
||||
|
||||
Selected calendars are checked for conflicts when calculating availability.
|
||||
|
||||
### List Selected Calendars
|
||||
|
||||
```http
|
||||
GET /v2/selected-calendars
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"integration": "google_calendar",
|
||||
"externalId": "primary",
|
||||
"credentialId": 123
|
||||
},
|
||||
{
|
||||
"integration": "google_calendar",
|
||||
"externalId": "work-calendar-id",
|
||||
"credentialId": 123
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## ICS Feed
|
||||
|
||||
### Check ICS Feed
|
||||
|
||||
```http
|
||||
GET /v2/calendars/ics-feed/check
|
||||
```
|
||||
|
||||
### Save ICS Feed
|
||||
|
||||
```http
|
||||
POST /v2/calendars/ics-feed/save
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://calendar.example.com/feed.ics"
|
||||
}
|
||||
```
|
||||
|
||||
## Calendar Events
|
||||
|
||||
### Get Calendar Event
|
||||
|
||||
```http
|
||||
GET /v2/calendars/{calendar}/events/{eventUid}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"id": "event-id-123",
|
||||
"title": "Meeting",
|
||||
"description": "Discussion",
|
||||
"start": {
|
||||
"time": "2024-01-15T10:00:00.000Z",
|
||||
"timeZone": "America/New_York"
|
||||
},
|
||||
"end": {
|
||||
"time": "2024-01-15T11:00:00.000Z",
|
||||
"timeZone": "America/New_York"
|
||||
},
|
||||
"attendees": [
|
||||
{
|
||||
"email": "attendee@example.com",
|
||||
"name": "Attendee Name",
|
||||
"responseStatus": "accepted"
|
||||
}
|
||||
],
|
||||
"status": "accepted",
|
||||
"source": "google"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Understanding Calendar Integration
|
||||
|
||||
### How Calendars Affect Availability
|
||||
|
||||
1. **Selected Calendars**: Events from selected calendars block availability
|
||||
2. **Destination Calendar**: New bookings are created in this calendar
|
||||
3. **Busy Times**: The API aggregates busy times from all selected calendars
|
||||
|
||||
### Calendar Sync Flow
|
||||
|
||||
```
|
||||
1. User connects calendar (OAuth)
|
||||
POST /v2/calendars/google-calendar/connect
|
||||
|
||||
2. User selects which calendars to check for conflicts
|
||||
(Done via Cal.com dashboard)
|
||||
|
||||
3. User sets destination calendar for new bookings
|
||||
(Done via Cal.com dashboard)
|
||||
|
||||
4. When checking slots:
|
||||
- API fetches busy times from all selected calendars
|
||||
- Busy times are excluded from available slots
|
||||
|
||||
5. When booking is created:
|
||||
- Event is created in destination calendar
|
||||
- Confirmation emails sent to attendees
|
||||
```
|
||||
|
||||
### Cal.com Event Identification
|
||||
|
||||
Cal.com events in external calendars can be identified by their iCalUID ending with `@Cal.com` (e.g., `2GBXSdEixretciJfKVmYN8@Cal.com`).
|
||||
|
||||
## Team Calendar Integration
|
||||
|
||||
For team-level calendar management:
|
||||
|
||||
### Team Conferencing
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/teams/{teamId}/conferencing
|
||||
```
|
||||
|
||||
### Connect Team Calendar
|
||||
|
||||
```http
|
||||
POST /v2/organizations/{orgId}/teams/{teamId}/conferencing/{app}/connect
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Check connection status**: Verify calendar is connected before operations
|
||||
2. **Handle OAuth expiry**: Calendar tokens may expire, handle re-authentication
|
||||
3. **Respect rate limits**: Calendar APIs have their own rate limits
|
||||
4. **Cache busy times**: Busy times don't change frequently, cache appropriately
|
||||
5. **Use webhooks**: Subscribe to booking events instead of polling calendars
|
||||
@@ -1,392 +0,0 @@
|
||||
# Event Types API Reference
|
||||
|
||||
Detailed documentation for event type endpoints in the Cal.com API v2.
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /v2/event-types | List event types |
|
||||
| POST | /v2/event-types | Create an event type |
|
||||
| GET | /v2/event-types/{eventTypeId} | Get an event type |
|
||||
| PATCH | /v2/event-types/{eventTypeId} | Update an event type |
|
||||
| DELETE | /v2/event-types/{eventTypeId} | Delete an event type |
|
||||
|
||||
## List Event Types
|
||||
|
||||
```http
|
||||
GET /v2/event-types
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| take | number | No | Number of results (default: 10, max: 250) |
|
||||
| skip | number | No | Pagination offset |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 123,
|
||||
"title": "30 Minute Meeting",
|
||||
"slug": "30min",
|
||||
"description": "A quick 30 minute call",
|
||||
"lengthInMinutes": 30,
|
||||
"locations": [
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "cal-video"
|
||||
}
|
||||
],
|
||||
"bookingFields": [],
|
||||
"disableGuests": false,
|
||||
"slotInterval": null,
|
||||
"minimumBookingNotice": 120,
|
||||
"beforeEventBuffer": 0,
|
||||
"afterEventBuffer": 0,
|
||||
"schedulingType": null,
|
||||
"metadata": {},
|
||||
"requiresConfirmation": false,
|
||||
"price": 0,
|
||||
"currency": "usd",
|
||||
"hidden": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Create an Event Type
|
||||
|
||||
```http
|
||||
POST /v2/event-types
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "30 Minute Meeting",
|
||||
"slug": "30min",
|
||||
"description": "A quick 30 minute call to discuss your needs",
|
||||
"lengthInMinutes": 30,
|
||||
"locations": [
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "cal-video"
|
||||
}
|
||||
],
|
||||
"bookingFields": [
|
||||
{
|
||||
"type": "textarea",
|
||||
"name": "notes",
|
||||
"label": "Additional Notes",
|
||||
"required": false,
|
||||
"placeholder": "Any additional information..."
|
||||
}
|
||||
],
|
||||
"disableGuests": false,
|
||||
"slotInterval": 15,
|
||||
"minimumBookingNotice": 120,
|
||||
"beforeEventBuffer": 5,
|
||||
"afterEventBuffer": 5,
|
||||
"scheduleId": 1,
|
||||
"requiresConfirmation": false,
|
||||
"hidden": false
|
||||
}
|
||||
```
|
||||
|
||||
### Required Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| title | string | Display name of the event type |
|
||||
| slug | string | URL-friendly identifier |
|
||||
| lengthInMinutes | number | Duration of the event |
|
||||
|
||||
### Optional Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| description | string | Description shown on booking page |
|
||||
| locations | array | Meeting location options |
|
||||
| bookingFields | array | Custom form fields |
|
||||
| disableGuests | boolean | Prevent attendees from adding guests |
|
||||
| slotInterval | number | Minutes between available slots |
|
||||
| minimumBookingNotice | number | Minimum minutes before booking |
|
||||
| beforeEventBuffer | number | Buffer time before event (minutes) |
|
||||
| afterEventBuffer | number | Buffer time after event (minutes) |
|
||||
| scheduleId | number | ID of schedule to use |
|
||||
| requiresConfirmation | boolean | Require host confirmation |
|
||||
| hidden | boolean | Hide from public profile |
|
||||
|
||||
## Location Types
|
||||
|
||||
### Cal Video (Built-in)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "cal-video"
|
||||
}
|
||||
```
|
||||
|
||||
### Zoom
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "zoom"
|
||||
}
|
||||
```
|
||||
|
||||
### Google Meet
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "google-meet"
|
||||
}
|
||||
```
|
||||
|
||||
### Microsoft Teams
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "integration",
|
||||
"integration": "msteams"
|
||||
}
|
||||
```
|
||||
|
||||
### In-Person
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "address",
|
||||
"address": "123 Main St, City, Country"
|
||||
}
|
||||
```
|
||||
|
||||
### Phone Call (Host Calls)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "userPhone"
|
||||
}
|
||||
```
|
||||
|
||||
### Phone Call (Attendee Provides)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "attendeePhone"
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Link
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "link",
|
||||
"link": "https://custom-meeting.com/room"
|
||||
}
|
||||
```
|
||||
|
||||
## Booking Fields
|
||||
|
||||
Custom form fields for collecting information from attendees.
|
||||
|
||||
### Text Field
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "text",
|
||||
"name": "company",
|
||||
"label": "Company Name",
|
||||
"required": true,
|
||||
"placeholder": "Enter your company name"
|
||||
}
|
||||
```
|
||||
|
||||
### Textarea
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "textarea",
|
||||
"name": "notes",
|
||||
"label": "Additional Notes",
|
||||
"required": false
|
||||
}
|
||||
```
|
||||
|
||||
### Select (Dropdown)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "select",
|
||||
"name": "topic",
|
||||
"label": "Meeting Topic",
|
||||
"required": true,
|
||||
"options": [
|
||||
{ "value": "sales", "label": "Sales Inquiry" },
|
||||
{ "value": "support", "label": "Support" },
|
||||
{ "value": "other", "label": "Other" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Radio Buttons
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "radio",
|
||||
"name": "preference",
|
||||
"label": "Preferred Contact Method",
|
||||
"required": true,
|
||||
"options": [
|
||||
{ "value": "email", "label": "Email" },
|
||||
{ "value": "phone", "label": "Phone" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Checkbox
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "checkbox",
|
||||
"name": "terms",
|
||||
"label": "I agree to the terms",
|
||||
"required": true
|
||||
}
|
||||
```
|
||||
|
||||
### Phone Number
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "phone",
|
||||
"name": "phone",
|
||||
"label": "Phone Number",
|
||||
"required": false
|
||||
}
|
||||
```
|
||||
|
||||
## Get an Event Type
|
||||
|
||||
```http
|
||||
GET /v2/event-types/{eventTypeId}
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| eventTypeId | number | Event type ID |
|
||||
|
||||
## Update an Event Type
|
||||
|
||||
```http
|
||||
PATCH /v2/event-types/{eventTypeId}
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
Only include fields you want to update:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Updated Meeting Title",
|
||||
"lengthInMinutes": 45,
|
||||
"hidden": false
|
||||
}
|
||||
```
|
||||
|
||||
## Delete an Event Type
|
||||
|
||||
```http
|
||||
DELETE /v2/event-types/{eventTypeId}
|
||||
```
|
||||
|
||||
## Team Event Types
|
||||
|
||||
For team event types, use the team-scoped endpoints:
|
||||
|
||||
### List Team Event Types
|
||||
|
||||
```http
|
||||
GET /v2/teams/{teamId}/event-types
|
||||
```
|
||||
|
||||
### Create Team Event Type
|
||||
|
||||
```http
|
||||
POST /v2/teams/{teamId}/event-types
|
||||
```
|
||||
|
||||
Additional fields for team event types:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Team Meeting",
|
||||
"slug": "team-meeting",
|
||||
"lengthInMinutes": 30,
|
||||
"schedulingType": "ROUND_ROBIN",
|
||||
"hosts": [
|
||||
{ "userId": 1, "isFixed": false },
|
||||
{ "userId": 2, "isFixed": false }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Scheduling Types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| ROUND_ROBIN | Distributes bookings among team members |
|
||||
| COLLECTIVE | All team members must attend |
|
||||
| MANAGED | Parent event type that creates child event types |
|
||||
|
||||
## Private Links
|
||||
|
||||
Create private booking links that bypass public visibility:
|
||||
|
||||
### List Private Links
|
||||
|
||||
```http
|
||||
GET /v2/event-types/{eventTypeId}/private-links
|
||||
```
|
||||
|
||||
### Create Private Link
|
||||
|
||||
```http
|
||||
POST /v2/event-types/{eventTypeId}/private-links
|
||||
```
|
||||
|
||||
## Event Type Webhooks
|
||||
|
||||
Configure webhooks specific to an event type:
|
||||
|
||||
### List Event Type Webhooks
|
||||
|
||||
```http
|
||||
GET /v2/event-types/{eventTypeId}/webhooks
|
||||
```
|
||||
|
||||
### Create Event Type Webhook
|
||||
|
||||
```http
|
||||
POST /v2/event-types/{eventTypeId}/webhooks
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"subscriberUrl": "https://your-app.com/webhook",
|
||||
"triggers": ["BOOKING_CREATED"],
|
||||
"active": true
|
||||
}
|
||||
```
|
||||
@@ -1,359 +0,0 @@
|
||||
# Schedules API Reference
|
||||
|
||||
Detailed documentation for schedule management endpoints in the Cal.com API v2.
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /v2/schedules | List all schedules |
|
||||
| POST | /v2/schedules | Create a schedule |
|
||||
| GET | /v2/schedules/default | Get default schedule |
|
||||
| GET | /v2/schedules/{scheduleId} | Get a schedule |
|
||||
| PATCH | /v2/schedules/{scheduleId} | Update a schedule |
|
||||
| DELETE | /v2/schedules/{scheduleId} | Delete a schedule |
|
||||
|
||||
## Understanding Schedules
|
||||
|
||||
Schedules define when a user is available for bookings. Key concepts:
|
||||
|
||||
- **Working Hours**: Regular weekly availability (e.g., Mon-Fri 9am-5pm)
|
||||
- **Date Overrides**: Exceptions to regular hours (e.g., holiday, special hours)
|
||||
- **Timezone**: The timezone in which availability is defined
|
||||
- **Default Schedule**: The primary schedule used when no specific schedule is assigned
|
||||
|
||||
## List Schedules
|
||||
|
||||
```http
|
||||
GET /v2/schedules
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Working Hours",
|
||||
"isDefault": true,
|
||||
"timeZone": "America/New_York",
|
||||
"workingHours": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": 540,
|
||||
"endTime": 1020
|
||||
}
|
||||
],
|
||||
"availability": [
|
||||
{
|
||||
"id": 1,
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "1970-01-01T09:00:00.000Z",
|
||||
"endTime": "1970-01-01T17:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"dateOverrides": [],
|
||||
"isManaged": false,
|
||||
"readOnly": false,
|
||||
"isLastSchedule": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Create a Schedule
|
||||
|
||||
```http
|
||||
POST /v2/schedules
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Working Hours",
|
||||
"timeZone": "America/New_York",
|
||||
"isDefault": true,
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "09:00",
|
||||
"endTime": "17:00"
|
||||
}
|
||||
],
|
||||
"dateOverrides": [
|
||||
{
|
||||
"date": "2024-12-25",
|
||||
"startTime": null,
|
||||
"endTime": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| name | string | Yes | Schedule name |
|
||||
| timeZone | string | Yes | IANA timezone identifier |
|
||||
| isDefault | boolean | No | Set as default schedule |
|
||||
| availability | array | Yes | Weekly availability rules |
|
||||
| dateOverrides | array | No | Date-specific overrides |
|
||||
|
||||
### Availability Object
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| days | array | Days of week (0=Sunday, 1=Monday, ..., 6=Saturday) |
|
||||
| startTime | string | Start time in HH:MM format |
|
||||
| endTime | string | End time in HH:MM format |
|
||||
|
||||
### Date Override Object
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| date | string | Date in YYYY-MM-DD format |
|
||||
| startTime | string/null | Start time (null = unavailable) |
|
||||
| endTime | string/null | End time (null = unavailable) |
|
||||
|
||||
## Get Default Schedule
|
||||
|
||||
```http
|
||||
GET /v2/schedules/default
|
||||
```
|
||||
|
||||
Returns the user's default schedule.
|
||||
|
||||
## Get a Schedule
|
||||
|
||||
```http
|
||||
GET /v2/schedules/{scheduleId}
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| scheduleId | number | Schedule ID |
|
||||
|
||||
## Update a Schedule
|
||||
|
||||
```http
|
||||
PATCH /v2/schedules/{scheduleId}
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
Only include fields you want to update:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Updated Schedule Name",
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "08:00",
|
||||
"endTime": "18:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Delete a Schedule
|
||||
|
||||
```http
|
||||
DELETE /v2/schedules/{scheduleId}
|
||||
```
|
||||
|
||||
Note: You cannot delete your last schedule. At least one schedule must exist.
|
||||
|
||||
## Common Schedule Patterns
|
||||
|
||||
### Standard Business Hours (Mon-Fri 9-5)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Business Hours",
|
||||
"timeZone": "America/New_York",
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "09:00",
|
||||
"endTime": "17:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Split Schedule (Morning and Afternoon)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Split Hours",
|
||||
"timeZone": "America/New_York",
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "09:00",
|
||||
"endTime": "12:00"
|
||||
},
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "14:00",
|
||||
"endTime": "18:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Different Hours Per Day
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Variable Hours",
|
||||
"timeZone": "America/New_York",
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 3, 5],
|
||||
"startTime": "09:00",
|
||||
"endTime": "17:00"
|
||||
},
|
||||
{
|
||||
"days": [2, 4],
|
||||
"startTime": "10:00",
|
||||
"endTime": "19:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Weekend Availability
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Weekend Support",
|
||||
"timeZone": "America/New_York",
|
||||
"availability": [
|
||||
{
|
||||
"days": [0, 6],
|
||||
"startTime": "10:00",
|
||||
"endTime": "14:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Holiday Override (Unavailable)
|
||||
|
||||
```json
|
||||
{
|
||||
"dateOverrides": [
|
||||
{
|
||||
"date": "2024-12-25",
|
||||
"startTime": null,
|
||||
"endTime": null
|
||||
},
|
||||
{
|
||||
"date": "2024-01-01",
|
||||
"startTime": null,
|
||||
"endTime": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Special Hours Override
|
||||
|
||||
```json
|
||||
{
|
||||
"dateOverrides": [
|
||||
{
|
||||
"date": "2024-12-24",
|
||||
"startTime": "09:00",
|
||||
"endTime": "12:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Organization/Team Schedules
|
||||
|
||||
For organization-level schedule management:
|
||||
|
||||
### List Organization Schedules
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/schedules
|
||||
```
|
||||
|
||||
### List User Schedules in Organization
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/users/{userId}/schedules
|
||||
```
|
||||
|
||||
### Create User Schedule in Organization
|
||||
|
||||
```http
|
||||
POST /v2/organizations/{orgId}/users/{userId}/schedules
|
||||
```
|
||||
|
||||
### Team Member Schedules
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/teams/{teamId}/users/{userId}/schedules
|
||||
```
|
||||
|
||||
## Working Hours Format
|
||||
|
||||
The API returns working hours in two formats:
|
||||
|
||||
### Minutes from Midnight
|
||||
|
||||
```json
|
||||
{
|
||||
"workingHours": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": 540,
|
||||
"endTime": 1020
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `540` = 9:00 AM (9 * 60 minutes)
|
||||
- `1020` = 5:00 PM (17 * 60 minutes)
|
||||
|
||||
### ISO Time Format
|
||||
|
||||
```json
|
||||
{
|
||||
"availability": [
|
||||
{
|
||||
"days": [1, 2, 3, 4, 5],
|
||||
"startTime": "1970-01-01T09:00:00.000Z",
|
||||
"endTime": "1970-01-01T17:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
When creating/updating, use HH:MM format:
|
||||
|
||||
```json
|
||||
{
|
||||
"startTime": "09:00",
|
||||
"endTime": "17:00"
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always specify timezone**: Schedules are timezone-aware
|
||||
2. **Use date overrides sparingly**: For recurring patterns, create separate schedules
|
||||
3. **Test availability**: After creating a schedule, verify with the slots endpoint
|
||||
4. **Consider buffer times**: Set buffers on event types, not schedules
|
||||
@@ -1,255 +0,0 @@
|
||||
# Slots and Availability API Reference
|
||||
|
||||
Detailed documentation for checking availability and managing slots in the Cal.com API v2.
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /v2/slots | Get available time slots |
|
||||
| POST | /v2/slots/reservations | Reserve a slot temporarily |
|
||||
| DELETE | /v2/slots/reservations/{uid} | Release a reserved slot |
|
||||
| GET | /v2/calendars/busy-times | Get busy times from calendars |
|
||||
|
||||
## Get Available Slots
|
||||
|
||||
Check available time slots for booking an event type.
|
||||
|
||||
```http
|
||||
GET /v2/slots
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| startTime | string | Yes | ISO 8601 start of date range |
|
||||
| endTime | string | Yes | ISO 8601 end of date range |
|
||||
| eventTypeId | number | Conditional | Event type ID (required if no slug) |
|
||||
| eventTypeSlug | string | Conditional | Event type slug (required if no ID) |
|
||||
| usernameList | string | Conditional | Comma-separated usernames for team events |
|
||||
| timeZone | string | No | Timezone for slot display (default: UTC) |
|
||||
| duration | number | No | Override event duration in minutes |
|
||||
| rescheduleUid | string | No | Booking UID if rescheduling |
|
||||
|
||||
### Example Request
|
||||
|
||||
```http
|
||||
GET /v2/slots?startTime=2024-01-15T00:00:00Z&endTime=2024-01-22T00:00:00Z&eventTypeId=123&timeZone=America/New_York
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"slots": {
|
||||
"2024-01-15": [
|
||||
{
|
||||
"time": "2024-01-15T09:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"time": "2024-01-15T09:30:00.000Z"
|
||||
},
|
||||
{
|
||||
"time": "2024-01-15T10:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"2024-01-16": [
|
||||
{
|
||||
"time": "2024-01-16T09:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response with Attendees (Seated Events)
|
||||
|
||||
For event types with `seatsPerTimeSlot` configured:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"slots": {
|
||||
"2024-01-15": [
|
||||
{
|
||||
"time": "2024-01-15T09:00:00.000Z",
|
||||
"attendees": 3,
|
||||
"seatsAvailable": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Reserve a Slot
|
||||
|
||||
Temporarily reserve a slot while the user completes the booking form. This prevents double-booking.
|
||||
|
||||
```http
|
||||
POST /v2/slots/reservations
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"eventTypeId": 123,
|
||||
"slotUtcStartDate": "2024-01-15T09:00:00.000Z",
|
||||
"slotUtcEndDate": "2024-01-15T09:30:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"uid": "reservation-uid-123",
|
||||
"eventTypeId": 123,
|
||||
"slotUtcStartDate": "2024-01-15T09:00:00.000Z",
|
||||
"slotUtcEndDate": "2024-01-15T09:30:00.000Z",
|
||||
"expiresAt": "2024-01-15T08:10:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reservations automatically expire after a short period (typically 10 minutes).
|
||||
|
||||
## Release a Reserved Slot
|
||||
|
||||
Release a slot reservation if the user abandons the booking flow.
|
||||
|
||||
```http
|
||||
DELETE /v2/slots/reservations/{uid}
|
||||
```
|
||||
|
||||
### Path Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| uid | string | Reservation UID |
|
||||
|
||||
## Get Busy Times
|
||||
|
||||
Check busy times from connected calendars.
|
||||
|
||||
```http
|
||||
GET /v2/calendars/busy-times
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| startTime | string | Yes | ISO 8601 start of date range |
|
||||
| endTime | string | Yes | ISO 8601 end of date range |
|
||||
| loggedInUsersTz | string | No | User's timezone |
|
||||
| credentialId | number | No | Specific calendar credential ID |
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"start": "2024-01-15T10:00:00.000Z",
|
||||
"end": "2024-01-15T11:00:00.000Z",
|
||||
"title": "Existing Meeting",
|
||||
"source": "google_calendar"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Routing Form Slots
|
||||
|
||||
For routing forms that direct to different event types:
|
||||
|
||||
```http
|
||||
POST /v2/routing-forms/{routingFormId}/calculate-slots
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"startTime": "2024-01-15T00:00:00Z",
|
||||
"endTime": "2024-01-22T00:00:00Z",
|
||||
"timeZone": "America/New_York",
|
||||
"responses": {
|
||||
"field1": "value1",
|
||||
"field2": "value2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Understanding Slot Availability
|
||||
|
||||
Slots are calculated based on:
|
||||
|
||||
1. **User's Schedule**: Working hours defined in their schedule
|
||||
2. **Existing Bookings**: Times already booked
|
||||
3. **Calendar Busy Times**: Events from connected calendars
|
||||
4. **Buffer Times**: Before/after event buffers
|
||||
5. **Minimum Notice**: Minimum booking notice period
|
||||
6. **Booking Limits**: Daily/weekly/monthly booking limits
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Efficient Slot Fetching
|
||||
|
||||
1. **Limit date range**: Request only the dates you need to display
|
||||
2. **Cache results**: Slots don't change frequently, cache for short periods
|
||||
3. **Use timezone parameter**: Request slots in user's timezone to avoid conversion
|
||||
|
||||
### Preventing Double Bookings
|
||||
|
||||
1. **Reserve slots**: Use slot reservations for multi-step booking flows
|
||||
2. **Handle expiration**: Reservations expire - handle gracefully
|
||||
3. **Verify before booking**: Always verify slot is still available before creating booking
|
||||
|
||||
### Example Booking Flow
|
||||
|
||||
```
|
||||
1. User selects date range
|
||||
GET /v2/slots?startTime=...&endTime=...&eventTypeId=123
|
||||
|
||||
2. User selects a slot
|
||||
POST /v2/slots/reservations
|
||||
{
|
||||
"eventTypeId": 123,
|
||||
"slotUtcStartDate": "2024-01-15T09:00:00Z",
|
||||
"slotUtcEndDate": "2024-01-15T09:30:00Z"
|
||||
}
|
||||
|
||||
3. User fills booking form
|
||||
|
||||
4. Create booking
|
||||
POST /v2/bookings
|
||||
{
|
||||
"start": "2024-01-15T09:00:00Z",
|
||||
"eventTypeId": 123,
|
||||
"attendee": {...}
|
||||
}
|
||||
|
||||
5. If user abandons, release reservation
|
||||
DELETE /v2/slots/reservations/{uid}
|
||||
```
|
||||
|
||||
## Timezone Handling
|
||||
|
||||
All times in the API are in UTC (ISO 8601 format). Use the `timeZone` parameter to receive slots in a specific timezone for display purposes.
|
||||
|
||||
```http
|
||||
GET /v2/slots?startTime=2024-01-15T00:00:00Z&endTime=2024-01-22T00:00:00Z&eventTypeId=123&timeZone=Europe/London
|
||||
```
|
||||
|
||||
The response times will still be in UTC, but the slot calculation will respect the user's timezone for determining available hours.
|
||||
@@ -1,330 +0,0 @@
|
||||
# Webhooks API Reference
|
||||
|
||||
Detailed documentation for webhook management endpoints in the Cal.com API v2.
|
||||
|
||||
## Endpoints Overview
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | /v2/webhooks | List webhooks |
|
||||
| POST | /v2/webhooks | Create a webhook |
|
||||
| GET | /v2/webhooks/{webhookId} | Get a webhook |
|
||||
| PATCH | /v2/webhooks/{webhookId} | Update a webhook |
|
||||
| DELETE | /v2/webhooks/{webhookId} | Delete a webhook |
|
||||
|
||||
## List Webhooks
|
||||
|
||||
```http
|
||||
GET /v2/webhooks
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": "webhook-id-123",
|
||||
"subscriberUrl": "https://your-app.com/webhook",
|
||||
"triggers": ["BOOKING_CREATED", "BOOKING_CANCELLED"],
|
||||
"active": true,
|
||||
"payloadTemplate": null,
|
||||
"secret": "whsec_..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Create a Webhook
|
||||
|
||||
```http
|
||||
POST /v2/webhooks
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"subscriberUrl": "https://your-app.com/webhook",
|
||||
"triggers": ["BOOKING_CREATED", "BOOKING_CANCELLED", "BOOKING_RESCHEDULED"],
|
||||
"active": true,
|
||||
"payloadTemplate": null,
|
||||
"secret": "your-webhook-secret"
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| subscriberUrl | string | Yes | URL to receive webhook payloads |
|
||||
| triggers | array | Yes | Events that trigger the webhook |
|
||||
| active | boolean | No | Enable/disable webhook (default: true) |
|
||||
| payloadTemplate | string | No | Custom payload template |
|
||||
| secret | string | No | Secret for signature verification |
|
||||
|
||||
## Webhook Triggers
|
||||
|
||||
| Trigger | Description |
|
||||
|---------|-------------|
|
||||
| BOOKING_CREATED | New booking created |
|
||||
| BOOKING_CANCELLED | Booking cancelled |
|
||||
| BOOKING_RESCHEDULED | Booking rescheduled |
|
||||
| BOOKING_CONFIRMED | Pending booking confirmed |
|
||||
| BOOKING_REJECTED | Booking rejected |
|
||||
| BOOKING_REQUESTED | Booking request received (requires confirmation) |
|
||||
| BOOKING_PAYMENT_INITIATED | Payment started |
|
||||
| BOOKING_NO_SHOW_UPDATED | Attendee marked as no-show |
|
||||
| MEETING_STARTED | Video meeting started |
|
||||
| MEETING_ENDED | Video meeting ended |
|
||||
| RECORDING_READY | Meeting recording available |
|
||||
| INSTANT_MEETING | Instant meeting created |
|
||||
| RECORDING_TRANSCRIPTION_GENERATED | Transcription ready |
|
||||
| FORM_SUBMITTED | Routing form submitted |
|
||||
|
||||
## Webhook Payload
|
||||
|
||||
### BOOKING_CREATED Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"triggerEvent": "BOOKING_CREATED",
|
||||
"createdAt": "2024-01-15T10:00:00.000Z",
|
||||
"payload": {
|
||||
"type": "30min",
|
||||
"title": "30 Minute Meeting",
|
||||
"description": "Meeting description",
|
||||
"startTime": "2024-01-15T10:00:00.000Z",
|
||||
"endTime": "2024-01-15T10:30:00.000Z",
|
||||
"organizer": {
|
||||
"id": 123,
|
||||
"name": "Jane Smith",
|
||||
"email": "jane@company.com",
|
||||
"timeZone": "America/New_York"
|
||||
},
|
||||
"attendees": [
|
||||
{
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"timeZone": "America/New_York"
|
||||
}
|
||||
],
|
||||
"location": "https://cal.com/video/abc123",
|
||||
"destinationCalendar": {
|
||||
"integration": "google_calendar",
|
||||
"externalId": "calendar-id"
|
||||
},
|
||||
"uid": "booking-uid-123",
|
||||
"metadata": {},
|
||||
"responses": {
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### BOOKING_CANCELLED Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"triggerEvent": "BOOKING_CANCELLED",
|
||||
"createdAt": "2024-01-15T12:00:00.000Z",
|
||||
"payload": {
|
||||
"uid": "booking-uid-123",
|
||||
"title": "30 Minute Meeting",
|
||||
"startTime": "2024-01-15T10:00:00.000Z",
|
||||
"endTime": "2024-01-15T10:30:00.000Z",
|
||||
"cancellationReason": "Schedule conflict",
|
||||
"organizer": {...},
|
||||
"attendees": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### BOOKING_RESCHEDULED Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"triggerEvent": "BOOKING_RESCHEDULED",
|
||||
"createdAt": "2024-01-15T11:00:00.000Z",
|
||||
"payload": {
|
||||
"uid": "new-booking-uid",
|
||||
"rescheduleUid": "original-booking-uid",
|
||||
"title": "30 Minute Meeting",
|
||||
"startTime": "2024-01-16T14:00:00.000Z",
|
||||
"endTime": "2024-01-16T14:30:00.000Z",
|
||||
"reschedulingReason": "Conflict with another meeting",
|
||||
"organizer": {...},
|
||||
"attendees": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Signature Verification
|
||||
|
||||
Webhooks include a signature header for verification:
|
||||
|
||||
```
|
||||
X-Cal-Signature-256: sha256=<signature>
|
||||
```
|
||||
|
||||
### Verification Example (Node.js)
|
||||
|
||||
```javascript
|
||||
const crypto = require('crypto');
|
||||
|
||||
function verifyWebhookSignature(payload, signature, secret) {
|
||||
const expectedSignature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(payload)
|
||||
.digest('hex');
|
||||
|
||||
return `sha256=${expectedSignature}` === signature;
|
||||
}
|
||||
|
||||
// In your webhook handler
|
||||
app.post('/webhook', (req, res) => {
|
||||
const signature = req.headers['x-cal-signature-256'];
|
||||
const payload = JSON.stringify(req.body);
|
||||
|
||||
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
|
||||
return res.status(401).send('Invalid signature');
|
||||
}
|
||||
|
||||
// Process webhook
|
||||
const { triggerEvent, payload: data } = req.body;
|
||||
// ...
|
||||
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
```
|
||||
|
||||
## Get a Webhook
|
||||
|
||||
```http
|
||||
GET /v2/webhooks/{webhookId}
|
||||
```
|
||||
|
||||
## Update a Webhook
|
||||
|
||||
```http
|
||||
PATCH /v2/webhooks/{webhookId}
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"active": false,
|
||||
"triggers": ["BOOKING_CREATED"]
|
||||
}
|
||||
```
|
||||
|
||||
## Delete a Webhook
|
||||
|
||||
```http
|
||||
DELETE /v2/webhooks/{webhookId}
|
||||
```
|
||||
|
||||
## Event Type Webhooks
|
||||
|
||||
Create webhooks specific to an event type:
|
||||
|
||||
### List Event Type Webhooks
|
||||
|
||||
```http
|
||||
GET /v2/event-types/{eventTypeId}/webhooks
|
||||
```
|
||||
|
||||
### Create Event Type Webhook
|
||||
|
||||
```http
|
||||
POST /v2/event-types/{eventTypeId}/webhooks
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"subscriberUrl": "https://your-app.com/webhook",
|
||||
"triggers": ["BOOKING_CREATED"],
|
||||
"active": true
|
||||
}
|
||||
```
|
||||
|
||||
## Organization Webhooks
|
||||
|
||||
For organization-wide webhooks:
|
||||
|
||||
### List Organization Webhooks
|
||||
|
||||
```http
|
||||
GET /v2/organizations/{orgId}/webhooks
|
||||
```
|
||||
|
||||
### Create Organization Webhook
|
||||
|
||||
```http
|
||||
POST /v2/organizations/{orgId}/webhooks
|
||||
```
|
||||
|
||||
## OAuth Client Webhooks
|
||||
|
||||
For platform integrations:
|
||||
|
||||
### List OAuth Client Webhooks
|
||||
|
||||
```http
|
||||
GET /v2/oauth-clients/{clientId}/webhooks
|
||||
```
|
||||
|
||||
### Create OAuth Client Webhook
|
||||
|
||||
```http
|
||||
POST /v2/oauth-clients/{clientId}/webhooks
|
||||
```
|
||||
|
||||
## Custom Payload Templates
|
||||
|
||||
Customize webhook payloads using templates:
|
||||
|
||||
```json
|
||||
{
|
||||
"subscriberUrl": "https://your-app.com/webhook",
|
||||
"triggers": ["BOOKING_CREATED"],
|
||||
"payloadTemplate": "{\"event\": \"{{triggerEvent}}\", \"booking_id\": \"{{payload.uid}}\", \"attendee\": \"{{payload.attendees[0].email}}\"}"
|
||||
}
|
||||
```
|
||||
|
||||
### Available Template Variables
|
||||
|
||||
- `{{triggerEvent}}` - Event type
|
||||
- `{{payload.uid}}` - Booking UID
|
||||
- `{{payload.title}}` - Event title
|
||||
- `{{payload.startTime}}` - Start time
|
||||
- `{{payload.endTime}}` - End time
|
||||
- `{{payload.organizer.name}}` - Organizer name
|
||||
- `{{payload.organizer.email}}` - Organizer email
|
||||
- `{{payload.attendees[0].name}}` - First attendee name
|
||||
- `{{payload.attendees[0].email}}` - First attendee email
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always verify signatures**: Use the webhook secret to verify payloads
|
||||
2. **Respond quickly**: Return 200 within 5 seconds, process async if needed
|
||||
3. **Handle retries**: Webhooks are retried on failure, implement idempotency
|
||||
4. **Use HTTPS**: Always use HTTPS endpoints for security
|
||||
5. **Log payloads**: Store webhook payloads for debugging
|
||||
6. **Monitor failures**: Track webhook delivery failures
|
||||
|
||||
## Retry Policy
|
||||
|
||||
Failed webhook deliveries are retried with exponential backoff:
|
||||
|
||||
- 1st retry: 1 minute
|
||||
- 2nd retry: 5 minutes
|
||||
- 3rd retry: 30 minutes
|
||||
- 4th retry: 2 hours
|
||||
- 5th retry: 24 hours
|
||||
|
||||
After 5 failed attempts, the webhook is marked as failed.
|
||||
Reference in New Issue
Block a user