refactor: Cal.diy (#28903)
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main: - Rebrand Cal.com to Cal.diy across the entire codebase - Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions - Switch license from AGPL-3.0 to MIT - Remove docs/ directory (migrated to Nextra at cal.diy) - Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc. - Clean up .env.example for self-hosted Cal.diy - Update Docker image references to calcom/cal.diy - Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork - Add PR welcome bot for Cal.diy contributors - Fix API v2 breaking changes oasdiff ignore entries - Replace Blacksmith CI runners with default GitHub Actions 3893 files changed, 20789 insertions(+), 411020 deletions(-) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * rip out org related comments in api v2 --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
a17f28e9ab
commit
ab21c7f805
4080 files changed
+14403
-404922
No files matched your search
@@ -1,21 +0,0 @@
|
||||
---
|
||||
title: "Authentication"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**API v1 is deprecated and will be discontinued on February 15, 2026.** Please migrate to [API v2](/api-reference/v2/introduction) as soon as possible.
|
||||
</Warning>
|
||||
|
||||
The Cal.com API uses API keys to authenticate requests. You can view and manage your API keys in your settings page under the security tab in Cal.com.
|
||||
|
||||
<img src="/images/i1600x899-DllqhV6w_3Vj_oxtjov.png" />
|
||||
|
||||
API Keys are under Settings > Security
|
||||
|
||||
Test mode secret keys have the prefix `cal_` and live mode secret keys have the prefix `cal_live_`.
|
||||
|
||||
Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
|
||||
|
||||
Authentication to the API is performed via URL Query Params. Provide your API key as a query param like: `https://api.cal.com/v1/?apiKey=cal_live_xxxxxx`
|
||||
|
||||
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
title: "Errors"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**API v1 is deprecated and will be discontinued on February 15, 2026.** Please migrate to [API v2](/api-reference/v2/introduction) as soon as possible.
|
||||
</Warning>
|
||||
|
||||
Cal.com uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted, or something else failed, etc.). Codes in the 5xx range indicate an error with Cal.com's servers (these are rare).
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>HTTP</th>
|
||||
<th>Status Code</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>200</td>
|
||||
<td>OK</td>
|
||||
<td>Everything worked as expected</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>400</td>
|
||||
<td>Bad Request</td>
|
||||
<td>The request was unacceptable, often due to missing a required parameter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>401</td>
|
||||
<td>Unauthorized</td>
|
||||
<td>No valid API key provided.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>402</td>
|
||||
<td>Request Failed</td>
|
||||
<td>The parameters were valid but the request failed.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>403</td>
|
||||
<td>Forbidden</td>
|
||||
<td>The API key doesn't have permissions to perform the request.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>404</td>
|
||||
<td>Not Found</td>
|
||||
<td>The requested resource doesn't exist.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>429</td>
|
||||
<td>Too Many Requests</td>
|
||||
<td>Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>500, 502, 503, 504</td>
|
||||
<td>erver Errors</td>
|
||||
<td>Something went wrong on our servers end. (These are rare.)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
title: "Quick start"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**API v1 is deprecated and will be discontinued on February 15, 2026.** Please migrate to [API v2](/api-reference/v2/introduction) as soon as possible.
|
||||
</Warning>
|
||||
|
||||
## Get your API keys
|
||||
|
||||
Your API requests are authenticated using API keys. Any request that doesn't include an API key will return an error.
|
||||
|
||||
You can generate an API key from **Settings >** [**Security**](https://app.cal.com/settings/security) within the application. There you will find the API keys section which allows you to generate a key for use.
|
||||
|
||||
More information on this can be found in the [Authentication](authentication) page.
|
||||
|
||||
## Install the library
|
||||
|
||||
The best way to interact with our API is using a good HTTP client like **axios**:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Node">
|
||||
```
|
||||
# Install via NPM
|
||||
npm install --save axios
|
||||
|
||||
# Install via Yarn
|
||||
yarn add axios
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Python">
|
||||
```
|
||||
# Install via pip
|
||||
pip install --upgrade requests
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Make your first request
|
||||
|
||||
To make your first request, send an authenticated request to the event-types endpoint. This will list all of your event types.
|
||||
|
||||
Example using `curl`:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Curl">
|
||||
```
|
||||
curl https://api.cal.com/v1/event-types?apiKey=cal_test_xxxxxx
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Try our Postman Collection
|
||||
|
||||
We've created a small collection of frequently made API calls that you can try in our [Postman Collection](https://www.postman.com/calcom/workspace/cd670dd8-b36d-4aee-8ab2-9044122e30d1/collection/20666831-b92a1d73-bfcc-4a5f-9ef5-cb117d49c904)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,43 +0,0 @@
|
||||
---
|
||||
title: "Rate limits"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**API v1 is deprecated and will be discontinued on February 15, 2026.** Please migrate to [API v2](/api-reference/v2/introduction) as soon as possible.
|
||||
</Warning>
|
||||
|
||||
We limit the number of calls you can make over a certain period of time. Rate limits vary and are specified by the following header in all responses:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>X-RateLimit-Limit</td>
|
||||
<td>The maximum number of requests that the consumer is permitted to make.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>X-RateLimit-Remaining</td>
|
||||
<td>The number of requests remaining in the current rate limit window.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>X-RateLimit-Reset</td>
|
||||
<td>The time at which the current rate limit window resets in UTC epoch seconds.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
When the rate limit is **exceeded**, an error is returned with the status **"429 Too Many Requests"**:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "too_many_requests",
|
||||
"message": "Rate limit exceeded",
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
title: "Introduction to API v2"
|
||||
description: "Introduction to Cal.com API v2 endpoints"
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
The Cal.com API has 3 authentication methods:
|
||||
|
||||
1. OAuth (Recommended)
|
||||
2. API key
|
||||
3. Platform (Deprecated)
|
||||
|
||||
### 1. OAuth [Recommended]
|
||||
|
||||
In order to be listed as an official partner and App in our App Store: cal.com/apps you need to create and get a verified OAuth client.
|
||||
|
||||
<Note>
|
||||
**Create an OAuth client and "Continue with Cal.com"**
|
||||
</Note>
|
||||
|
||||
You can request it here: https://cal.com/docs/api-reference/v2/oauth.
|
||||
|
||||
### 2. API key
|
||||
|
||||
While API keys can be created easily, bear in mind we almost always recommend using OAuth credentials, especially when building integrations or applications with Cal.com.
|
||||
|
||||
You can view and manage your API keys in your settings page under the security tab in Cal.com.
|
||||
|
||||

|
||||
|
||||
API Keys are under Settings \> Security
|
||||
|
||||
Test mode secret keys have the prefix `cal_` and live mode secret keys have the prefix `cal_live_`.
|
||||
|
||||
Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
|
||||
|
||||
Authentication to the API is performed via the Authorization header. For example, the request would go something like:
|
||||
|
||||
```
|
||||
'Authorization': 'Bearer YOUR_API_KEY'
|
||||
```
|
||||
|
||||
in your request header.
|
||||
|
||||
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.
|
||||
|
||||
## Teams endpoints
|
||||
|
||||
Teams customers have all the endpoints except the ones prefixed with "Platform" and "Orgs".
|
||||
|
||||
## Organizations endpoints
|
||||
|
||||
Organizations customers have all the endpoints except the ones prefixed with "Teams" and "Orgs / Orgs".
|
||||
|
||||
## Rate limits
|
||||
|
||||
There are three authentication methods for the API, and each of them has the following rate limits:
|
||||
|
||||
1. API Key - 120 requests per minute. This can be increased to a reasonable amount, such as 200 requests per minute. If you require a higher rate limit, such as 800 requests per minute, it is possible, but it may involve extra charges. To request this, please contact support.
|
||||
|
||||
If no authentication method is provided, the default rate limit is 120 requests per minute.
|
||||
|
||||
## Deprecated & Maintenance for existing users only
|
||||
|
||||
<Warning>
|
||||
As of 15th December 2025, we're currently undergoing a restructuring of our "Platform"-offering. Until further we continue to provide enterprise support for existing customers but no longer offer new signups for any "Platform" plan.
|
||||
</Warning>
|
||||
|
||||
### 1. Platform OAuth client credentials [Deprecated]
|
||||
|
||||
You need to use OAuth credentials when:
|
||||
|
||||
1. Managing managed users [API reference](https://cal.com/docs/api-reference/v2/platform-managed-users/create-a-managed-user)
|
||||
2. Creating OAuth client webhooks [API reference](https://cal.com/docs/api-reference/v2/platform-webhooks/create-a-webhook)
|
||||
3. Refreshing tokens of a managed user [API reference](https://cal.com/docs/api-reference/v2/platform-managed-users/refresh-managed-user-tokens)
|
||||
4. Teams related endpoints: Managing organization teams [API reference](https://cal.com/docs/api-reference/v2/orgs-teams/create-a-team), adding managed users as members to teams [API reference](https://cal.com/docs/api-reference/v2/orgs-teams-memberships/create-a-membership), creating team event types [API reference](https://cal.com/docs/api-reference/v2/orgs-event-types/create-an-event-type).
|
||||
|
||||
OAuth credentials can be accessed in the platform dashboard https://app.cal.com/settings/platform after you have created an OAuth client. Each one has an ID and secret. You then need to pass them as request headers:
|
||||
|
||||
1. `x-cal-client-id` - ID of the OAuth client.
|
||||
2. `x-cal-secret-key` - secret of the OAuth client.
|
||||
|
||||
### 2. Platform Managed user access token [Deprecated]
|
||||
|
||||
After you create a managed user you will receive its access and refresh tokens. The response also includes managed user's id, so we recommend you to add new properties to your users table calAccessToken, calRefreshToken and calManagedUserId to store this information.
|
||||
|
||||
You need to use access token when managing managed user's:
|
||||
|
||||
1. Schedules [API reference](https://cal.com/docs/api-reference/v2/schedules/create-a-schedule)
|
||||
2. Event types [API reference](https://cal.com/docs/api-reference/v2/event-types/create-an-event-type)
|
||||
3. Bookings - some endpoints like creating a booking is public, but some like getting all managed user's bookings require managed user's access token [API reference](https://cal.com/docs/api-reference/v2/bookings/get-all-bookings)
|
||||
|
||||
It is passed as an authorization bearer request header Authorization: Bearer \<access-token\>.
|
||||
|
||||
Validity period: access tokens are valid for 60 minutes and refresh tokens for 1 year, and tokens can be refreshed using the refresh endpoint [API reference](https://cal.com/docs/api-reference/v2/oauth/post-v2oauth-refresh). After refreshing you will receive the new access and refresh tokens that you have to store in your database.
|
||||
|
||||
Recovering tokens: if you ever lose managed user's access or refresh tokens, you can force refresh them using the OAuth client credentials and store them in your database [API reference](https://cal.com/docs/api-reference/v2/platform-managed-users/force-refresh-tokens).
|
||||
|
||||
### Platform endpoints [Deprecated]
|
||||
|
||||
Platform customers have the following endpoints available:
|
||||
|
||||
1. Endpoints prefixed with "Platform".
|
||||
2. Endpoints with no prefix e.g "Bookings", "Event Types".
|
||||
3. If you are at least on the ESSENTIALS plan, then all endpoints prefixed with "Orgs" except "Orgs / Attributes", "Orgs / Attributes / Options" and "Orgs / Teams / Routing forms / Responses".
|
||||
@@ -1,361 +0,0 @@
|
||||
---
|
||||
title: "Migrating from V1 to V2"
|
||||
description: "A comprehensive guide to help you migrate from Cal.com API 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>
|
||||
|
||||
This guide will help you migrate your integration from Cal.com API V1 to V2. The V2 API offers improved performance, enhanced security, better response objects, and new features that make it easier to build powerful integrations.
|
||||
|
||||
## Why migrate to V2?
|
||||
|
||||
- **Better performance**: Optimized for speed and scalability
|
||||
- **Enhanced security**: Improved authentication methods and security measures
|
||||
- **User-friendly responses**: Cleaner, more consistent response objects
|
||||
- **New features**: Access to organization management, and more
|
||||
|
||||
## Authentication changes
|
||||
|
||||
### V1 authentication
|
||||
|
||||
In V1, you authenticated using an API key in the query string:
|
||||
|
||||
```bash
|
||||
curl https://api.cal.com/v1/event-types?apiKey=cal_test_xxxxxx
|
||||
```
|
||||
|
||||
### V2 authentication
|
||||
|
||||
In V2, authentication uses header-based API keys for improved security:
|
||||
|
||||
```bash
|
||||
curl https://api.cal.com/v2/event-types \
|
||||
-H "Authorization: Bearer cal_live_xxxxxx"
|
||||
```
|
||||
|
||||
## Base URL changes
|
||||
|
||||
| Version | Base URL |
|
||||
| ------- | ------------------------ |
|
||||
| V1 | `https://api.cal.com/v1` |
|
||||
| V2 | `https://api.cal.com/v2` |
|
||||
|
||||
## Endpoint mapping
|
||||
|
||||
### Bookings
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| ----------------------- | ------------------------------------ | -------------------------------------------------- |
|
||||
| `POST /bookings` | `POST /v2/bookings` | Enhanced response object with more booking details |
|
||||
| `GET /bookings/{id}` | `GET /v2/bookings/{uid}` | Returns richer booking information |
|
||||
| `DELETE /bookings/{id}` | `POST /v2/bookings/{uid}/cancel` | Now uses POST with cancellation reason support |
|
||||
| `PATCH /bookings/{id}` | `POST /v2/bookings/{uid}/reschedule` | Rescheduling is now a dedicated endpoint |
|
||||
|
||||
**V1 example:**
|
||||
|
||||
```bash
|
||||
DELETE /v1/bookings/123?apiKey=cal_test_xxx
|
||||
```
|
||||
|
||||
**V2 example:**
|
||||
|
||||
```bash
|
||||
POST /v2/bookings/cal_abc123/cancel
|
||||
Authorization: Bearer cal_live_xxx
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"cancellationReason": "Schedule conflict"
|
||||
}
|
||||
```
|
||||
|
||||
### Event types
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| -------------------------- | ----------------------------- | --------------------------------- |
|
||||
| `GET /event-types` | `GET /v2/event-types` | Improved filtering and pagination |
|
||||
| `POST /event-types` | `POST /v2/event-types` | Enhanced configuration options |
|
||||
| `GET /event-types/{id}` | `GET /v2/event-types/{id}` | More detailed response |
|
||||
| `DELETE /event-types/{id}` | `DELETE /v2/event-types/{id}` | Same functionality |
|
||||
| `PATCH /event-types/{id}` | `PATCH /v2/event-types/{id}` | More update options available |
|
||||
|
||||
### Schedules
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| ------------------------ | --------------------------- | ----------------------------- |
|
||||
| `GET /schedules` | `GET /v2/schedules` | Better structured response |
|
||||
| `POST /schedules` | `POST /v2/schedules` | Simplified schedule creation |
|
||||
| `GET /schedules/{id}` | `GET /v2/schedules/{id}` | Enhanced availability details |
|
||||
| `DELETE /schedules/{id}` | `DELETE /v2/schedules/{id}` | Same functionality |
|
||||
| `PATCH /schedules/{id}` | `PATCH /v2/schedules/{id}` | More flexible updates |
|
||||
|
||||
### Users
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| ------------------- | -------------- | -------------------------------------- |
|
||||
| `GET /users` | `GET /v2/me` | Now returns authenticated user profile |
|
||||
| `GET /users/{id}` | `GET /v2/me` | User-specific endpoint for own profile |
|
||||
| `PATCH /users/{id}` | `PATCH /v2/me` | Update your own profile |
|
||||
|
||||
### Teams
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| -------------------- | ----------------------- | -------------------------- |
|
||||
| `GET /teams` | `GET /v2/teams` | Enhanced team information |
|
||||
| `POST /teams` | `POST /v2/teams` | More configuration options |
|
||||
| `GET /teams/{id}` | `GET /v2/teams/{id}` | Richer team details |
|
||||
| `DELETE /teams/{id}` | `DELETE /v2/teams/{id}` | Same functionality |
|
||||
| `PATCH /teams/{id}` | `PATCH /v2/teams/{id}` | More update options |
|
||||
|
||||
### Webhooks
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| ----------------------- | -------------------------- | -------------------------- |
|
||||
| `GET /webhooks` | `GET /v2/webhooks` | Better webhook management |
|
||||
| `POST /webhooks` | `POST /v2/webhooks` | More event types available |
|
||||
| `GET /webhooks/{id}` | `GET /v2/webhooks/{id}` | Enhanced webhook details |
|
||||
| `DELETE /webhooks/{id}` | `DELETE /v2/webhooks/{id}` | Same functionality |
|
||||
| `PATCH /webhooks/{id}` | `PATCH /v2/webhooks/{id}` | More configuration options |
|
||||
|
||||
### Slots
|
||||
|
||||
| V1 Endpoint | V2 Endpoint | Changes |
|
||||
| ---------------------- | -------------------------------- | --------------------------------------------- |
|
||||
| `GET /slots/available` | `GET /v2/slots/available` | Improved slot calculation and response format |
|
||||
| N/A | `POST /v2/slots/reserve` | New: Reserve slots before booking |
|
||||
| N/A | `DELETE /v2/slots/reserve/{uid}` | New: Release reserved slots |
|
||||
|
||||
## New features in V2
|
||||
|
||||
### Organization management
|
||||
|
||||
V2 introduces comprehensive organization endpoints for enterprise customers:
|
||||
|
||||
- `GET /v2/orgs/teams` - Manage organization teams
|
||||
- `GET /v2/orgs/users` - Manage organization users
|
||||
- `GET /v2/orgs/bookings` - View all organization bookings
|
||||
- `GET /v2/orgs/memberships` - Manage team memberships
|
||||
- `GET /v2/orgs/roles` - Manage custom roles and permissions
|
||||
|
||||
### Enhanced booking management
|
||||
|
||||
- `POST /v2/bookings/{uid}/confirm` - Confirm pending bookings
|
||||
- `POST /v2/bookings/{uid}/decline` - Decline booking requests
|
||||
- `POST /v2/bookings/{uid}/mark-absent` - Mark no-shows
|
||||
- `POST /v2/bookings/{uid}/reassign` - Reassign bookings to different hosts
|
||||
- `POST /v2/bookings/{uid}/guests` - Add guests to existing bookings
|
||||
|
||||
### Calendar integrations
|
||||
|
||||
- `GET /v2/calendars` - List connected calendars
|
||||
- `GET /v2/calendars/busy-times` - Check availability across calendars
|
||||
- `POST /v2/calendars/connect` - Connect calendar providers
|
||||
- `POST /v2/calendars/disconnect` - Disconnect calendars
|
||||
|
||||
### Conferencing apps
|
||||
|
||||
- `GET /v2/conferencing` - List conferencing apps
|
||||
- `POST /v2/conferencing/connect` - Connect conferencing providers
|
||||
- `POST /v2/conferencing/set-default` - Set default conferencing app
|
||||
|
||||
## Response format changes
|
||||
|
||||
### V1 response structure
|
||||
|
||||
```json
|
||||
{
|
||||
"booking": {
|
||||
"id": 123,
|
||||
"title": "Meeting",
|
||||
"startTime": "2024-01-15T10:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### V2 response structure
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"data": {
|
||||
"id": 123,
|
||||
"uid": "cal_abc123xyz",
|
||||
"title": "Meeting",
|
||||
"startTime": "2024-01-15T10:00:00.000Z",
|
||||
"endTime": "2024-01-15T11:00:00.000Z",
|
||||
"status": "accepted",
|
||||
"attendees": [...],
|
||||
"metadata": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Key improvements:
|
||||
|
||||
- Consistent `status` and `data` wrapper
|
||||
- More detailed timestamps with milliseconds
|
||||
- Additional metadata and context
|
||||
- Better error messages with specific error codes
|
||||
|
||||
## Error handling
|
||||
|
||||
### V1 errors
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Event type not found"
|
||||
}
|
||||
```
|
||||
|
||||
### V2 errors
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"error": {
|
||||
"code": "NOT_FOUND",
|
||||
"message": "Event type not found",
|
||||
"details": {
|
||||
"eventTypeId": 123
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
V2 provides:
|
||||
|
||||
- Structured error codes for programmatic handling
|
||||
- More descriptive error messages
|
||||
- Additional context in error details
|
||||
- Consistent error format across all endpoints
|
||||
|
||||
## Rate limits
|
||||
|
||||
| Authentication Method | V1 Rate Limit | V2 Rate Limit |
|
||||
| --------------------- | ------------- | ------------- |
|
||||
| API Key | 120 req/min | 120 req/min |
|
||||
|
||||
## Migration checklist
|
||||
|
||||
<Steps>
|
||||
<Step title="Update authentication">
|
||||
Move API keys from query parameters to Authorization headers
|
||||
</Step>
|
||||
<Step title="Update base URLs">
|
||||
Change all API calls from `/v1/` to `/v2/`
|
||||
</Step>
|
||||
<Step title="Update endpoint paths">
|
||||
Review the endpoint mapping table and update your API calls
|
||||
</Step>
|
||||
<Step title="Update request/response handling">
|
||||
Adapt your code to handle the new response structure with `status` and `data` fields
|
||||
</Step>
|
||||
<Step title="Update error handling">
|
||||
Implement handling for the new structured error format
|
||||
</Step>
|
||||
<Step title="Test thoroughly">
|
||||
Test all API integrations in a development environment before deploying to production
|
||||
</Step>
|
||||
<Step title="Monitor and optimize">
|
||||
Take advantage of new features to optimize your integration
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Code examples
|
||||
|
||||
### Creating a booking
|
||||
|
||||
**V1:**
|
||||
|
||||
```javascript
|
||||
const response = await fetch(
|
||||
'https://api.cal.com/v1/bookings?apiKey=cal_test_xxx',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
eventTypeId: 123,
|
||||
start: '2024-01-15T10:00:00Z',
|
||||
responses: {
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com'
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**V2:**
|
||||
|
||||
```javascript
|
||||
const response = await fetch(
|
||||
'https://api.cal.com/v2/bookings',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer cal_live_xxx',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
eventTypeId: 123,
|
||||
start: '2024-01-15T10:00:00.000Z',
|
||||
attendee: {
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
timeZone: 'America/New_York'
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
const data = await response.json();
|
||||
if (data.status === 'success') {
|
||||
console.log('Booking created:', data.data);
|
||||
}
|
||||
```
|
||||
|
||||
### Fetching event types
|
||||
|
||||
**V1:**
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
response = requests.get(
|
||||
'https://api.cal.com/v1/event-types',
|
||||
params={'apiKey': 'cal_test_xxx'}
|
||||
)
|
||||
event_types = response.json()['event_types']
|
||||
```
|
||||
|
||||
**V2:**
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
response = requests.get(
|
||||
'https://api.cal.com/v2/event-types',
|
||||
headers={'Authorization': 'Bearer cal_live_xxx'}
|
||||
)
|
||||
|
||||
data = response.json()
|
||||
if data['status'] == 'success':
|
||||
event_types = data['data']
|
||||
```
|
||||
|
||||
## Getting help
|
||||
|
||||
If you encounter issues during migration:
|
||||
|
||||
1. Check the [V2 API Reference](/docs/api-reference/v2/introduction) for detailed endpoint documentation
|
||||
2. Review the [differences between V1 and V2](/docs/api-reference/v2/v1-v2-differences)
|
||||
3. Contact [Cal.com support](https://go.cal.com/support) for migration assistance
|
||||
|
||||
## Timeline
|
||||
|
||||
- **Now**: V2 is available and recommended for all new integrations
|
||||
- **February 15, 2026**: V1 will be discontinued
|
||||
|
||||
Start your migration today to ensure a smooth transition and take advantage of V2's enhanced capabilities.
|
||||
@@ -1,451 +0,0 @@
|
||||
---
|
||||
title: "OAuth"
|
||||
description: "Authorize apps with Cal.com accounts using OAuth"
|
||||
---
|
||||
|
||||
As an example, you can view our OAuth flow in action on Zapier. Try to connect your Cal.com account [here](https://zapier.com/apps/calcom/integrations). To enable OAuth in one of your apps, you will need a Client ID, Client Secret, Authorization URL, Access Token Request URL, and Refresh Token Request URL.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
#### Get your OAuth "Continue with [Cal.com](http://Cal.com)" Badge
|
||||
|
||||
- https://app.cal.com/continue-with-calcom-coss-ui.svg
|
||||
- https://app.cal.com/continue-with-calcom-dark-rounded.svg
|
||||
- https://app.cal.com/continue-with-calcom-dark-squared.svg
|
||||
- https://app.cal.com/continue-with-calcom-light-rounded.svg
|
||||
- https://app.cal.com/continue-with-calcom-light-squared.svg
|
||||
- https://app.cal.com/continue-with-calcom-neutral-rounded.svg
|
||||
- https://app.cal.com/continue-with-calcom-light-squared.svg
|
||||
|
||||
|
||||
## 1. OAuth Client Credentials
|
||||
|
||||
You can create an OAuth client via the following page https://app.cal.com/settings/developer/oauth. The OAuth client will be in a "pending" state
|
||||
and not yet ready to use.
|
||||
|
||||
An admin from Cal.com will then review your OAuth client and you will receive an email if it was accepted or rejected. If it was accepted then your OAuth client
|
||||
is ready to be used.
|
||||
|
||||
## 2. Authorize
|
||||
|
||||
To initiate the OAuth flow, direct users to the following authorization URL:
|
||||
|
||||
`https://app.cal.com/auth/oauth2/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&state=YOUR_STATE`
|
||||
|
||||
**URL Parameters:**
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `client_id` | Yes | Your OAuth client ID |
|
||||
| `redirect_uri` | Yes | Where users will be redirected after authorization. Must exactly match the registered redirect URI. |
|
||||
| `state` | Recommended | A securely generated random string to mitigate CSRF attacks |
|
||||
| `code_challenge` | For public clients | PKCE code challenge (S256 method) |
|
||||
|
||||
After users click **Allow**, they will be redirected to the `redirect_uri` with `code` (authorization code) and `state` as URL parameters:
|
||||
|
||||
```
|
||||
https://your-app.com/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE
|
||||
```
|
||||
|
||||
#### Error Handling
|
||||
|
||||
Errors during the authorization step are displayed directly to the user on the Cal.com authorization page. Your application will not receive a JSON error response for these cases:
|
||||
|
||||
- **Client not found**: No OAuth client exists with the provided `client_id`.
|
||||
- **Client not approved**: The OAuth client has not been approved by a Cal.com admin yet.
|
||||
- **Mismatched redirect URI**: The `redirect_uri` does not match the one registered for the OAuth client.
|
||||
|
||||
If an error occurs after the client is validated (e.g., the user denies access or has insufficient permissions), the user is redirected to the `redirect_uri` with an error:
|
||||
|
||||
```
|
||||
https://your-app.com/callback?error=access_denied&error_description=team_not_found_or_no_access&state=YOUR_STATE
|
||||
```
|
||||
|
||||
## 3. Exchange Token
|
||||
|
||||
Exchange an authorization code for access and refresh tokens. The token endpoint also accepts `application/x-www-form-urlencoded` content type.
|
||||
|
||||
**Endpoint:** `POST https://api.cal.com/v2/auth/oauth2/token`
|
||||
|
||||
### 3.1 Confidential Clients
|
||||
|
||||
Confidential clients authenticate with a `client_secret`. All parameters are required:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `client_id` | Your OAuth client ID |
|
||||
| `client_secret` | Your OAuth client secret |
|
||||
| `grant_type` | Must be `authorization_code` |
|
||||
| `code` | The authorization code received in the redirect URI |
|
||||
| `redirect_uri` | Must match the redirect URI used in the authorization request |
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl -X POST https://api.cal.com/v2/auth/oauth2/token \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_id": "YOUR_CLIENT_ID",
|
||||
"client_secret": "YOUR_CLIENT_SECRET",
|
||||
"grant_type": "authorization_code",
|
||||
"code": "AUTHORIZATION_CODE",
|
||||
"redirect_uri": "https://your-app.com/callback"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="fetch">
|
||||
```typescript
|
||||
const response = await fetch("https://api.cal.com/v2/auth/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
client_secret: "YOUR_CLIENT_SECRET",
|
||||
grant_type: "authorization_code",
|
||||
code: "AUTHORIZATION_CODE",
|
||||
redirect_uri: "https://your-app.com/callback",
|
||||
}),
|
||||
});
|
||||
|
||||
const tokens = await response.json();
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="axios">
|
||||
```typescript
|
||||
import axios from "axios";
|
||||
|
||||
const { data } = await axios.post(
|
||||
"https://api.cal.com/v2/auth/oauth2/token",
|
||||
{
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
client_secret: "YOUR_CLIENT_SECRET",
|
||||
grant_type: "authorization_code",
|
||||
code: "AUTHORIZATION_CODE",
|
||||
redirect_uri: "https://your-app.com/callback",
|
||||
}
|
||||
);
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 3.2 Public Clients (PKCE)
|
||||
|
||||
Public clients (e.g. single-page apps, mobile apps) use PKCE instead of a `client_secret`. You must have sent a `code_challenge` during the authorization step. All parameters are required:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `client_id` | Your OAuth client ID |
|
||||
| `grant_type` | Must be `authorization_code` |
|
||||
| `code` | The authorization code received in the redirect URI |
|
||||
| `redirect_uri` | Must match the redirect URI used in the authorization request |
|
||||
| `code_verifier` | The original PKCE code verifier used to generate the `code_challenge` |
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl -X POST https://api.cal.com/v2/auth/oauth2/token \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_id": "YOUR_CLIENT_ID",
|
||||
"grant_type": "authorization_code",
|
||||
"code": "AUTHORIZATION_CODE",
|
||||
"redirect_uri": "https://your-app.com/callback",
|
||||
"code_verifier": "YOUR_CODE_VERIFIER"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="fetch">
|
||||
```typescript
|
||||
const response = await fetch("https://api.cal.com/v2/auth/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
grant_type: "authorization_code",
|
||||
code: "AUTHORIZATION_CODE",
|
||||
redirect_uri: "https://your-app.com/callback",
|
||||
code_verifier: "YOUR_CODE_VERIFIER",
|
||||
}),
|
||||
});
|
||||
|
||||
const tokens = await response.json();
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="axios">
|
||||
```typescript
|
||||
import axios from "axios";
|
||||
|
||||
const { data } = await axios.post(
|
||||
"https://api.cal.com/v2/auth/oauth2/token",
|
||||
{
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
grant_type: "authorization_code",
|
||||
code: "AUTHORIZATION_CODE",
|
||||
redirect_uri: "https://your-app.com/callback",
|
||||
code_verifier: "YOUR_CODE_VERIFIER",
|
||||
}
|
||||
);
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
#### Success Response (200)
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 1800
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
Access tokens expire after 30 minutes (`expires_in: 1800`). Use the refresh token to obtain a new access token.
|
||||
</Note>
|
||||
|
||||
#### Error Responses
|
||||
|
||||
Error responses include `error` and `error_description` fields.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Invalid or expired authorization code (400)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_grant",
|
||||
"error_description": "code_invalid_or_expired"
|
||||
}
|
||||
```
|
||||
The authorization code has already been used, has expired, or is invalid. Request a new authorization code.
|
||||
</Accordion>
|
||||
<Accordion title="Invalid client credentials (401)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_client",
|
||||
"error_description": "invalid_client_credentials"
|
||||
}
|
||||
```
|
||||
The `client_secret` does not match the `client_id`. Verify your credentials.
|
||||
</Accordion>
|
||||
<Accordion title="Client not found (401)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_client",
|
||||
"error_description": "client_not_found"
|
||||
}
|
||||
```
|
||||
No OAuth client exists with the provided `client_id`.
|
||||
</Accordion>
|
||||
<Accordion title="Missing client_id (400)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "client_id is required"
|
||||
}
|
||||
```
|
||||
The `client_id` field is missing from the request body.
|
||||
</Accordion>
|
||||
<Accordion title="Invalid grant_type (400)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "grant_type must be 'authorization_code' or 'refresh_token'"
|
||||
}
|
||||
```
|
||||
The `grant_type` field must be either `authorization_code` or `refresh_token`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 4. Refresh Token
|
||||
|
||||
Refresh an expired access token using a refresh token.
|
||||
|
||||
**Endpoint:** `POST https://api.cal.com/v2/auth/oauth2/token`
|
||||
|
||||
### 4.1 Confidential Clients
|
||||
|
||||
Confidential clients authenticate with a `client_secret`. All parameters are required:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `client_id` | Your OAuth client ID |
|
||||
| `client_secret` | Your OAuth client secret |
|
||||
| `grant_type` | Must be `refresh_token` |
|
||||
| `refresh_token` | The refresh token received from a previous token response |
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl -X POST https://api.cal.com/v2/auth/oauth2/token \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_id": "YOUR_CLIENT_ID",
|
||||
"client_secret": "YOUR_CLIENT_SECRET",
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": "YOUR_REFRESH_TOKEN"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="fetch">
|
||||
```typescript
|
||||
const response = await fetch("https://api.cal.com/v2/auth/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
client_secret: "YOUR_CLIENT_SECRET",
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: "YOUR_REFRESH_TOKEN",
|
||||
}),
|
||||
});
|
||||
|
||||
const tokens = await response.json();
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="axios">
|
||||
```typescript
|
||||
import axios from "axios";
|
||||
|
||||
const { data } = await axios.post(
|
||||
"https://api.cal.com/v2/auth/oauth2/token",
|
||||
{
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
client_secret: "YOUR_CLIENT_SECRET",
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: "YOUR_REFRESH_TOKEN",
|
||||
}
|
||||
);
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 4.2 Public Clients
|
||||
|
||||
Public clients do not use a `client_secret`. All parameters are required:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `client_id` | Your OAuth client ID |
|
||||
| `grant_type` | Must be `refresh_token` |
|
||||
| `refresh_token` | The refresh token received from a previous token response |
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl -X POST https://api.cal.com/v2/auth/oauth2/token \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_id": "YOUR_CLIENT_ID",
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": "YOUR_REFRESH_TOKEN"
|
||||
}'
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="fetch">
|
||||
```typescript
|
||||
const response = await fetch("https://api.cal.com/v2/auth/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: "YOUR_REFRESH_TOKEN",
|
||||
}),
|
||||
});
|
||||
|
||||
const tokens = await response.json();
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="axios">
|
||||
```typescript
|
||||
import axios from "axios";
|
||||
|
||||
const { data } = await axios.post(
|
||||
"https://api.cal.com/v2/auth/oauth2/token",
|
||||
{
|
||||
client_id: "YOUR_CLIENT_ID",
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: "YOUR_REFRESH_TOKEN",
|
||||
}
|
||||
);
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
#### Success Response (200)
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 1800
|
||||
}
|
||||
```
|
||||
|
||||
#### Error Responses
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Invalid refresh token (400)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_grant",
|
||||
"error_description": "invalid_refresh_token"
|
||||
}
|
||||
```
|
||||
The refresh token is invalid, expired, or malformed. The user must re-authorize.
|
||||
</Accordion>
|
||||
<Accordion title="Invalid client credentials (401)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_client",
|
||||
"error_description": "invalid_client_credentials"
|
||||
}
|
||||
```
|
||||
The `client_secret` does not match the `client_id`.
|
||||
</Accordion>
|
||||
<Accordion title="Client not found (401)">
|
||||
```json
|
||||
{
|
||||
"error": "invalid_client",
|
||||
"error_description": "client_not_found"
|
||||
}
|
||||
```
|
||||
No OAuth client exists with the provided `client_id`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 5. Verify Access Token
|
||||
|
||||
To verify the correct setup and functionality of OAuth credentials, use the following endpoint:
|
||||
|
||||
**Endpoint:** `GET https://api.cal.com/v2/me`
|
||||
|
||||
<Tabs>
|
||||
<Tab title="cURL">
|
||||
```bash
|
||||
curl -X GET https://api.cal.com/v2/me \
|
||||
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="fetch">
|
||||
```typescript
|
||||
const response = await fetch("https://api.cal.com/v2/me", {
|
||||
headers: { Authorization: "Bearer YOUR_ACCESS_TOKEN" },
|
||||
});
|
||||
|
||||
const user = await response.json();
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="axios">
|
||||
```typescript
|
||||
import axios from "axios";
|
||||
|
||||
const { data } = await axios.get("https://api.cal.com/v2/me", {
|
||||
headers: { Authorization: "Bearer YOUR_ACCESS_TOKEN" },
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
+658
-19239
File diff suppressed because it is too large.
Load diff
@@ -1,504 +0,0 @@
|
||||
---
|
||||
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.
|
||||
Reference in new issue
Block a user