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