Documentation Index
Fetch the complete documentation index at: https://calcomhelp-mintlify-onboarding-embed-1775554265.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
The onboarding embed allows you to add a “Continue with Cal.com” button to your application. When a user clicks it, a dialog opens where they can sign up for Cal.com, set up their profile, connect a calendar, and authorize your app with OAuth — all in one flow, without ever leaving your site.
This is useful if you are building an integration that needs access to a user’s Cal.com account. Instead of sending users to Cal.com separately, you can embed the entire signup and authorization experience directly in your app.
The onboarding embed is a React component available in the @calcom/atoms package. You need an OAuth client ID from Cal.com to use it. See OAuth setup for details on obtaining credentials.
How it works
The onboarding embed guides users through four steps inside a modal dialog:
- Sign up or log in — The user creates a new Cal.com account or signs in to an existing one.
- Profile setup — The user enters their name, username, and bio.
- Calendar connection — The user optionally connects a calendar (Google Calendar, Apple Calendar, etc.).
- OAuth authorization — The user reviews the permissions your app is requesting and clicks “Allow” or “Deny”.
If a user closes the dialog before finishing, they can reopen it and resume where they left off.
Installation
Install the @calcom/atoms package:
npm install @calcom/atoms
Basic usage
Import the OnboardingEmbed component and add it to your page:
import { OnboardingEmbed } from "@calcom/atoms";
function App() {
const state = crypto.randomUUID();
return (
<OnboardingEmbed
oAuthClientId="your-client-id"
authorization={{
redirectUri: "https://yourapp.com/callback",
scope: ["EVENT_TYPE_READ", "BOOKING_READ"],
state: state,
}}
onAuthorizationAllowed={({ code }) => {
// Exchange this code for an access token on your server
console.log("Authorization code:", code);
}}
onAuthorizationDenied={() => {
console.log("User denied access");
}}
onError={(error) => {
console.error("Onboarding error:", error);
}}
/>
);
}
By default, the component renders a “Continue with Cal.com” button. Clicking it opens the onboarding dialog.
Configuration
Required props
| Prop | Type | Description |
|---|
oAuthClientId | string | Your OAuth client ID registered with Cal.com. |
authorization | object | OAuth authorization parameters (see below). |
Authorization object
| Property | Type | Required | Description |
|---|
redirectUri | string | Yes | One of the redirect URIs registered on your OAuth client. Must share the same origin as the page hosting the embed. |
scope | string[] | Yes | OAuth scopes to request, such as EVENT_TYPE_READ, BOOKING_WRITE, or SCHEDULE_READ. |
state | string | Yes | A random string to prevent CSRF attacks. Use crypto.randomUUID() to generate one. |
codeChallenge | string | No | PKCE code challenge for public clients that cannot store a client secret. Uses the S256 method. |
Optional props
| Prop | Type | Default | Description |
|---|
host | string | https://app.cal.com | The Cal.com host URL. Change this if you are using a self-hosted instance. |
theme | "light" or "dark" | "light" | Controls the appearance of the button and dialog. |
user | object | — | Pre-fill the signup form with email, name, and username. |
trigger | ReactNode | ”Continue with Cal.com” button | A custom element to open the onboarding dialog. |
onAuthorizationAllowed | function | — | Called when the user clicks “Allow”. Receives { code } with the authorization code. |
onAuthorizationDenied | function | — | Called when the user clicks “Deny”. |
onError | function | — | Called when an error occurs. Receives an error object with code and message. |
onClose | function | — | Called when the dialog is dismissed. |
Authorization modes
The onboarding embed supports two ways to handle the authorization result.
Callback mode
If you provide the onAuthorizationAllowed prop, the component calls your function with the authorization code and closes the dialog. This keeps the user on your page.
<OnboardingEmbed
oAuthClientId="your-client-id"
authorization={{
redirectUri: "https://yourapp.com/callback",
scope: ["EVENT_TYPE_READ"],
state: crypto.randomUUID(),
}}
onAuthorizationAllowed={({ code }) => {
// Send the code to your server to exchange for tokens
fetch("/api/exchange-token", {
method: "POST",
body: JSON.stringify({ code }),
});
}}
onAuthorizationDenied={() => {
// Handle denial
}}
/>
Redirect mode
If you do not provide onAuthorizationAllowed, the browser redirects to your redirectUri with the authorization code and state as URL parameters:
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE
If the user denies access, the redirect includes an error parameter:
https://yourapp.com/callback?error=access_denied&state=YOUR_STATE
Pre-filling user details
You can pass a user prop to pre-fill the signup form:
<OnboardingEmbed
oAuthClientId="your-client-id"
user={{
email: "jane@company.com",
name: "Jane Smith",
username: "janesmith",
}}
authorization={{
redirectUri: "https://yourapp.com/callback",
scope: ["EVENT_TYPE_READ"],
state: crypto.randomUUID(),
}}
onAuthorizationAllowed={({ code }) => {
console.log("Authorization code:", code);
}}
/>
Using PKCE
For public clients that cannot securely store a client secret, use PKCE (Proof Key for Code Exchange) by including a codeChallenge in the authorization object:
<OnboardingEmbed
oAuthClientId="your-client-id"
authorization={{
redirectUri: "https://yourapp.com/callback",
scope: ["EVENT_TYPE_READ"],
state: crypto.randomUUID(),
codeChallenge: "your-generated-code-challenge",
}}
onAuthorizationAllowed={({ code }) => {
// Exchange the code along with your code_verifier
console.log("Authorization code:", code);
}}
/>
Custom trigger element
By default, the embed renders a “Continue with Cal.com” button. You can replace it with your own element:
<OnboardingEmbed
oAuthClientId="your-client-id"
authorization={{
redirectUri: "https://yourapp.com/callback",
scope: ["EVENT_TYPE_READ"],
state: crypto.randomUUID(),
}}
trigger={<button className="my-button">Connect your calendar</button>}
onAuthorizationAllowed={({ code }) => {
console.log("Authorization code:", code);
}}
/>
Error handling
The onError callback receives an error object with a code and message. The possible error codes are:
| Error code | Description |
|---|
INVALID_PROPS | Required props are missing (such as oAuthClientId or authorization). |
SIGNUP_FAILED | Account creation failed. |
ONBOARDING_FAILED | A step in the onboarding process failed. |
AUTHORIZATION_FAILED | The OAuth authorization step failed. |
STATE_MISMATCH | The state parameter in the response does not match the one you provided. This may indicate a CSRF attack. |
UNKNOWN | An unexpected error occurred. |
<OnboardingEmbed
oAuthClientId="your-client-id"
authorization={{
redirectUri: "https://yourapp.com/callback",
scope: ["EVENT_TYPE_READ"],
state: crypto.randomUUID(),
}}
onError={(error) => {
switch (error.code) {
case "INVALID_PROPS":
console.error("Check your component configuration");
break;
case "STATE_MISMATCH":
console.error("Possible security issue — state does not match");
break;
default:
console.error("Onboarding error:", error.message);
}
}}
onAuthorizationAllowed={({ code }) => {
console.log("Authorization code:", code);
}}
/>