Skip to main content
As an example, you can view our OAuth flow in action on Zapier. Try to connect your Cal.com account here. 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.

Get your OAuth “Continue with Cal.com” Badge

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:
ParameterRequiredDescription
client_idYesYour OAuth client ID
redirect_uriYesWhere users will be redirected after authorization. Must exactly match the registered redirect URI.
stateRecommendedA securely generated random string to mitigate CSRF attacks
code_challengeFor public clientsPKCE 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:
ParameterDescription
client_idYour OAuth client ID
client_secretYour OAuth client secret
grant_typeMust be authorization_code
codeThe authorization code received in the redirect URI
redirect_uriMust match the redirect URI used in the authorization request
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"
  }'

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:
ParameterDescription
client_idYour OAuth client ID
grant_typeMust be authorization_code
codeThe authorization code received in the redirect URI
redirect_uriMust match the redirect URI used in the authorization request
code_verifierThe original PKCE code verifier used to generate the code_challenge
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"
  }'

Success Response (200)

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 1800
}
Access tokens expire after 30 minutes (expires_in: 1800). Use the refresh token to obtain a new access token.

Error Responses

Error responses include error and error_description fields.
{
  "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.
{
  "error": "invalid_client",
  "error_description": "invalid_client_credentials"
}
The client_secret does not match the client_id. Verify your credentials.
{
  "error": "invalid_client",
  "error_description": "client_not_found"
}
No OAuth client exists with the provided client_id.
{
  "error": "invalid_request",
  "error_description": "client_id is required"
}
The client_id field is missing from the request body.
{
  "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.

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:
ParameterDescription
client_idYour OAuth client ID
client_secretYour OAuth client secret
grant_typeMust be refresh_token
refresh_tokenThe refresh token received from a previous token response
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"
  }'

4.2 Public Clients

Public clients do not use a client_secret. All parameters are required:
ParameterDescription
client_idYour OAuth client ID
grant_typeMust be refresh_token
refresh_tokenThe refresh token received from a previous token response
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"
  }'

Success Response (200)

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 1800
}

Error Responses

{
  "error": "invalid_grant",
  "error_description": "invalid_refresh_token"
}
The refresh token is invalid, expired, or malformed. The user must re-authorize.
{
  "error": "invalid_client",
  "error_description": "invalid_client_credentials"
}
The client_secret does not match the client_id.
{
  "error": "invalid_client",
  "error_description": "client_not_found"
}
No OAuth client exists with the provided client_id.

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
curl -X GET https://api.cal.com/v2/me \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"