Files
calendar/docs/api-reference/v2/v1-v2-differences.mdx
T
mintlify[bot]GitHubmintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>Volnei Munhoz
2bd7549f20 Add Cal.com API V1 to V2 Migration Guide (#25196)
* Update docs/api-reference/v2/migration-guide.mdx

* Update docs/api-reference/v2/v1-v2-differences.mdx

* Update docs/api-reference/v2/v1-v2-differences.mdx

* Update docs/api-reference/v2/migration-guide.mdx

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
2025-11-21 11:00:00 +00:00

505 lines
9.5 KiB
Plaintext

---
title: "Migrating from API v1 to v2"
description: "Complete guide for migrating your Cal.com integration from v1 to v2"
---
<Warning>
**API v1 is deprecated and will be discontinued on February 15, 2026.** Please migrate to API v2 as soon as possible.
</Warning>
## Why migrate to v2?
The v2 API includes numerous enhancements and new features that are not available in v1:
- **Performance improvements**: Optimized for better performance and scalability
- **User-friendly response objects**: Improved response structure for better developer experience
- **Enhanced security**: Improved security measures to protect your data
- **New features**: Access to new Cal.com features only available in v2
- **Better error handling**: More descriptive error messages and status codes
## Authentication changes
In v1, you authenticated using an API key passed as a query parameter:
```bash
curl https://api.cal.com/v1/bookings?apiKey=cal_test_xxxxxx
```
In v2, you authenticate using an API key in the `Authorization` header:
```bash
curl https://api.cal.com/v2/bookings \
-H "Authorization: Bearer cal_test_xxxxxx" \
-H "cal-api-version: 2024-08-13"
```
## Endpoint-by-endpoint migration guide
### Bookings
#### Create a booking
**V1 endpoint:**
```
POST /v1/bookings
```
**V1 request body:**
```json
{
"eventTypeId": 123,
"start": "2023-05-24T13:00:00.000Z",
"end": "2023-05-24T13:30:00.000Z",
"responses": {
"name": "John Doe",
"email": "[email protected]",
"location": {
"value": "userPhone",
"optionValue": ""
}
},
"timeZone": "Europe/London",
"language": "en",
"metadata": {}
}
```
**V2 endpoint:**
```
POST /v2/bookings
```
**V2 request body:**
```json
{
"eventTypeId": 123,
"start": "2024-08-13T09:00:00Z",
"attendee": {
"name": "John Doe",
"email": "[email protected]",
"timeZone": "America/New_York",
"language": "en"
},
"location": {
"type": "phone"
},
"metadata": {}
}
```
**Key differences:**
- `responses` object replaced with `attendee` object
- `end` time is no longer required (calculated from event type duration)
- `location` structure changed from `{value, optionValue}` to `{type}`
- Must include `cal-api-version: 2024-08-13` header
- Response structure is more detailed with `status` and `data` wrapper
**V1 response:**
```json
{
"booking": {
"id": 91,
"uid": "bFJeNb2uX8ANpT3JL5EfXw",
"startTime": "2023-05-25T09:30:00.000Z",
"endTime": "2023-05-25T10:30:00.000Z",
"attendees": [...],
"status": "ACCEPTED"
}
}
```
**V2 response:**
```json
{
"status": "success",
"data": {
"id": 123,
"uid": "booking_uid_123",
"start": "2024-08-13T15:30:00Z",
"end": "2024-08-13T16:30:00Z",
"duration": 60,
"status": "accepted",
"hosts": [...],
"attendees": [...],
"eventType": {
"id": 1,
"slug": "some-event"
}
}
}
```
#### Get all bookings
**V1:** Not available as a dedicated endpoint
**V2 endpoint:**
```
GET /v2/bookings
```
**V2 query parameters:**
- `status`: Filter by booking status (accepted, pending, cancelled, rejected)
- `attendeeEmail`: Filter by attendee email
- `eventTypeId`: Filter by event type
- `afterStart`, `beforeEnd`: Filter by date range
- `take`, `skip`: Pagination
- `sortStart`, `sortEnd`, `sortCreated`: Sorting options
**V2 response includes pagination:**
```json
{
"status": "success",
"data": [...],
"pagination": {
"totalItems": 123,
"currentPage": 2,
"totalPages": 13,
"hasNextPage": true
}
}
```
#### Cancel a booking
**V1 endpoint:**
```
DELETE /v1/bookings?id={id}&allRemainingBookings=false&cancellationReason=reason
```
**V2 endpoint:**
```
POST /v2/bookings/{uid}/cancel
```
**V2 request body:**
```json
{
"cancellationReason": "User requested cancellation"
}
```
**Key differences:**
- Changed from DELETE to POST method
- Uses booking `uid` in path instead of `id` query parameter
- Cancellation reason in request body instead of query parameter
#### Reschedule a booking
**V1:** Required creating a new booking with `rescheduleUid`
**V2 endpoint:**
```
POST /v2/bookings/{uid}/reschedule
```
**V2 request body:**
```json
{
"start": "2024-08-14T10:00:00Z",
"reschedulingReason": "Conflict with another meeting"
}
```
**Key differences:**
- Dedicated reschedule endpoint in v2
- Simpler process - just provide new start time
- Automatically handles the relationship between old and new bookings
### Event types
#### Get all event types
**V1 endpoint:**
```
GET /v1/event-types
```
**V2 endpoint:**
```
GET /v2/event-types
```
**Key differences:**
- V2 response includes more detailed information about each event type
- V2 includes pagination support
- V2 response wrapped in `{status, data}` structure
#### Create an event type
**V1 endpoint:**
```
POST /v1/event-types
```
**V1 request body:**
```json
{
"title": "30 Min Meeting",
"slug": "30min",
"length": 30,
"locations": [{"type": "integrations:zoom"}]
}
```
**V2 endpoint:**
```
POST /v2/event-types
```
**V2 request body:**
```json
{
"title": "30 Min Meeting",
"slug": "30min",
"lengthInMinutes": 30,
"locations": [{"type": "zoom"}]
}
```
**Key differences:**
- `length` renamed to `lengthInMinutes` for clarity
- Location types simplified (no `integrations:` prefix)
- More configuration options available in v2
#### Update an event type
**V1 endpoint:**
```
PATCH /v1/event-types/{id}
```
**V2 endpoint:**
```
PATCH /v2/event-types/{id}
```
**Key differences:**
- Same HTTP method and path structure
- Request/response body structure differences match create endpoint
- V2 provides more granular control over event type settings
### Schedules
#### Get all schedules
**V1 endpoint:**
```
GET /v1/schedules
```
**V2 endpoint:**
```
GET /v2/schedules
```
**Key differences:**
- V2 includes default schedule indicator
- V2 response includes more detailed availability information
- V2 supports filtering and pagination
#### Create a schedule
**V1 endpoint:**
```
POST /v1/schedules
```
**V1 request body:**
```json
{
"name": "Working Hours",
"timeZone": "America/New_York",
"availability": [...]
}
```
**V2 endpoint:**
```
POST /v2/schedules
```
**V2 request body:**
```json
{
"name": "Working Hours",
"timeZone": "America/New_York",
"isDefault": false,
"schedule": [...]
}
```
**Key differences:**
- `availability` renamed to `schedule` in v2
- `isDefault` flag added to set default schedule
- More flexible schedule configuration options
### Webhooks
#### Get all webhooks
**V1 endpoint:**
```
GET /v1/webhooks
```
**V2 endpoint:**
```
GET /v2/webhooks
```
#### Create a webhook
**V1 endpoint:**
```
POST /v1/webhooks
```
**V1 request body:**
```json
{
"subscriberUrl": "https://example.com/webhook",
"eventTriggers": ["BOOKING_CREATED"],
"active": true
}
```
**V2 endpoint:**
```
POST /v2/webhooks
```
**V2 request body:**
```json
{
"payloadTemplate": null,
"triggers": ["BOOKING_CREATED"],
"subscriberUrl": "https://example.com/webhook",
"active": true
}
```
**Key differences:**
- `eventTriggers` renamed to `triggers`
- Added `payloadTemplate` for custom webhook payloads
- More webhook event types available in v2
### Slots
#### Get available slots
**V1 endpoint:**
```
GET /v1/slots/available?eventTypeId=123&startTime=2024-01-01&endTime=2024-01-31
```
**V2 endpoint:**
```
GET /v2/slots/available?eventTypeId=123&startTime=2024-01-01T00:00:00Z&endTime=2024-01-31T23:59:59Z
```
**Key differences:**
- V2 requires full ISO 8601 timestamps
- V2 response includes more metadata about slot availability
- V2 supports additional filtering options (username, teamSlug, etc.)
### Teams
#### Get all teams
**V1 endpoint:**
```
GET /v1/teams
```
**V2 endpoint:**
```
GET /v2/teams
```
**Key differences:**
- V2 includes organization context if team is part of an org
- V2 response includes more team metadata
- V2 supports pagination
### Users
#### Get user profile
**V1 endpoint:**
```
GET /v1/users/{id}
```
**V2 endpoint:**
```
GET /v2/me
```
**Key differences:**
- V2 uses `/me` endpoint for current user
- V2 returns more detailed profile information
- V2 includes organization and team memberships
## Response structure changes
### V1 response format
```json
{
"booking": {...}
}
```
### V2 response format
```json
{
"status": "success",
"data": {...}
}
```
All v2 responses follow this consistent structure with:
- `status`: Either "success" or "error"
- `data`: The actual response data
- `error`: Error details (only present when status is "error")
## Error handling
### V1 errors
```json
{
"message": "Event type not found"
}
```
### V2 errors
```json
{
"status": "error",
"error": {
"code": "NOT_FOUND",
"message": "Event type not found"
}
}
```
V2 provides more structured error responses with error codes for better error handling.
## Migration checklist
- [ ] Update authentication to use `Authorization` header instead of query parameter
- [ ] Add `cal-api-version: 2024-08-13` header to all requests
- [ ] Update base URL from `/v1/` to `/v2/`
- [ ] Update request body structures (especially `responses` → `attendee` for bookings)
- [ ] Update response parsing to handle `{status, data}` wrapper
- [ ] Update location object structures
- [ ] Update field names (`length` → `lengthInMinutes`, `eventTriggers` → `triggers`, etc.)
- [ ] Implement pagination handling for list endpoints
- [ ] Update error handling to parse new error structure
- [ ] Test all endpoints in your integration
- [ ] Update any stored booking/event type IDs if needed
## Need help?
If you have questions or need assistance with migrating to v2, please contact our support team.