--- title: "Migrating from API v1 to v2" description: "Complete guide for migrating your Cal.com integration from v1 to v2" --- **API v1 is deprecated and will be discontinued on February 15, 2026.** Please migrate to API v2 as soon as possible. ## 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.