Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: v2 rolling window and authentication #17825

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ export class OAuthClientUsersController {

@Delete("/:userId")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Delete a managed user" })
@ApiOperation({
summary: "Delete a managed user",
})
async deleteUser(
@Param("clientId") clientId: string,
@Param("userId") userId: number
Expand All @@ -155,7 +157,11 @@ export class OAuthClientUsersController {

@Post("/:userId/force-refresh")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Force refresh tokens" })
@ApiOperation({
summary: "Force refresh tokens",
description: `If you have lost managed user access or refresh token, then you can get new ones by using OAuth credentials.
Each access token is valid for 60 minutes and each refresh token for 1 year. Make sure to store them later in your database, for example, by updating the User model to have \`calAccessToken\` and \`calRefreshToken\` columns.`,
})
async forceRefresh(
@Param("userId") userId: number,
@Param("clientId") oAuthClientId: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ApiExcludeEndpoint as DocsExcludeEndpoint,
ApiBadRequestResponse as DocsBadRequestResponse,
ApiHeader as DocsHeader,
ApiOperation,
} from "@nestjs/swagger";
import { Response as ExpressResponse } from "express";

Expand All @@ -39,7 +40,6 @@ import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
path: "/v2/oauth/:clientId",
version: API_VERSIONS_VALUES,
})
@DocsTags("OAuth")
export class OAuthFlowController {
constructor(
private readonly oauthClientRepository: OAuthClientRepository,
Expand Down Expand Up @@ -115,13 +115,18 @@ export class OAuthFlowController {
@Post("/refresh")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@DocsTags("Managed users")
@DocsTags("Platform / Managed Users")
@DocsHeader({
name: X_CAL_SECRET_KEY,
description: "OAuth client secret key.",
required: true,
})
async refreshAccessToken(
@ApiOperation({
summary: "Refresh managed user tokens",
description: `If managed user access token is expired then get a new one using this endpoint. Each access token is valid for 60 minutes and
each refresh token for 1 year. Make sure to store them later in your database, for example, by updating the User model to have \`calAccessToken\` and \`calRefreshToken\` columns.`,
})
async refreshTokens(
@Param("clientId") clientId: string,
@Headers(X_CAL_SECRET_KEY) secretKey: string,
@Body() body: RefreshTokenInput
Expand Down
106 changes: 54 additions & 52 deletions apps/api/v2/swagger/documentation.json
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@
"post": {
"operationId": "OAuthClientUsersController_forceRefresh",
"summary": "Force refresh tokens",
"description": "If you have lost managed user access or refresh token, then you can get new ones by using OAuth credentials.\n Each access token is valid for 60 minutes and each refresh token for 1 year. Make sure to store them later in your database, for example, by updating the User model to have `calAccessToken` and `calRefreshToken` columns.",
"parameters": [
{
"name": "userId",
Expand Down Expand Up @@ -397,6 +398,57 @@
]
}
},
"/v2/oauth/{clientId}/refresh": {
"post": {
"operationId": "OAuthFlowController_refreshTokens",
"summary": "Refresh managed user tokens",
"description": "If managed user access token is expired then get a new one using this endpoint. Each access token is valid for 60 minutes and \n each refresh token for 1 year. Make sure to store them later in your database, for example, by updating the User model to have `calAccessToken` and `calRefreshToken` columns.",
"parameters": [
{
"name": "clientId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "x-cal-secret-key",
"required": true,
"in": "header",
"description": "OAuth client secret key.",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshTokenInput"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/KeysResponseDto"
}
}
}
}
},
"tags": [
"Platform / Managed Users"
]
}
},
"/v2/oauth-clients/{clientId}/webhooks": {
"post": {
"operationId": "OAuthClientWebhooksController_createOAuthClientWebhook",
Expand Down Expand Up @@ -4439,56 +4491,6 @@
]
}
},
"/v2/oauth/{clientId}/refresh": {
"post": {
"operationId": "OAuthFlowController_refreshAccessToken",
"parameters": [
{
"name": "clientId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "x-cal-secret-key",
"required": true,
"in": "header",
"description": "OAuth client secret key.",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshTokenInput"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/KeysResponseDto"
}
}
}
}
},
"tags": [
"OAuth",
"Managed users"
]
}
},
"/v2/schedules": {
"post": {
"operationId": "SchedulesController_2024_06_11_createSchedule",
Expand Down Expand Up @@ -6532,7 +6534,7 @@
"rolling": {
"type": "boolean",
"example": true,
"description": "If true, the window will be rolling aka from the moment that someone is trying to book this event. Otherwise it will be specified amount of days from the current date."
"description": "\n Determines the behavior of the booking window:\n - If **true**, the window is rolling. This means the number of available days will always equal the specified 'value' \n and adjust dynamically as bookings are made. For example, if 'value' is 3 and availability is only on Mondays, \n a booker attempting to schedule on November 10 will see slots on November 11, 18, and 25. As one of these days \n becomes fully booked, a new day (e.g., December 2) will open up to ensure 3 available days are always visible.\n - If **false**, the window is fixed. This means the booking window only considers the next 'value' days from the\n moment someone is trying to book. For example, if 'value' is 3, availability is only on Mondays, and the current \n date is November 10, the booker will only see slots on November 11 because the window is restricted to the next \n 3 calendar days (November 10–12).\n "
keithwillcode marked this conversation as resolved.
Show resolved Hide resolved
}
},
"required": [
Expand Down Expand Up @@ -6560,7 +6562,7 @@
"rolling": {
"type": "boolean",
"example": true,
"description": "If true, the window will be rolling aka from the moment that someone is trying to book this event. Otherwise it will be specified amount of days from the current date."
"description": "\n Determines the behavior of the booking window:\n - If **true**, the window is rolling. This means the number of available days will always equal the specified 'value' \n and adjust dynamically as bookings are made. For example, if 'value' is 3 and availability is only on Mondays, \n a booker attempting to schedule on November 10 will see slots on November 11, 18, and 25. As one of these days \n becomes fully booked, a new day (e.g., December 2) will open up to ensure 3 available days are always visible.\n - If **false**, the window is fixed. This means the booking window only considers the next 'value' days from the\n moment someone is trying to book. For example, if 'value' is 3, availability is only on Mondays, and the current \n date is November 10, the booker will only see slots on November 11 because the window is restricted to the next \n 3 calendar days (November 10–12).\n "
}
},
"required": [
Expand Down
49 changes: 48 additions & 1 deletion docs/api-reference/v2/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ description: 'Introduction to Cal.com API v2 endpoints'

## Authentication

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.
The Cal.com API has 3 authentication methods:

1. API key
2. Platform OAuth client credentials
3. Managed user access token

If you are a platform customer you don't need an API key and must instead use OAuth credentials or a managed user access token. We cover when to use which below.

### 1. API key

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" />

Expand All @@ -24,3 +34,40 @@ Authentication to the API is performed via the Authorization header. For example
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.

### 2. OAuth client credentials
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/oauth/post-v2oauth-refresh)
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.

### 3. Managed user access token

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

## Navigating endpoints
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".

Non-platform customers have all the endpoints except the ones prefixed with "Platform".
Loading
Loading