Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Cal.diy User Guide

Generated June 2026 from calcom/cal.diy at d1ad4ea

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Cal.diy is the community-driven, fully open-source scheduling platform — a fork of Cal.com with all enterprise and commercial code removed. It lets people book time with you (or your team) through a personal booking page, embed that booking flow into any website, and build scheduling directly into your own product through React components and a REST API. Everything in this repository is MIT-licensed, requires no license key, and is designed to run on infrastructure you control.

This guide covers everything you can do with Cal.diy as a user, self-hoster, or developer integrating scheduling into your own app. Architecture, deployment internals, the full configuration reference, and security hardening live in the companion Admin Guide.

Warning

Cal.diy is the open-source community edition and is recommended for personal, non-production use. Self-hosting requires comfort with server administration, database management, and securing sensitive data. For commercial, enterprise-ready scheduling infrastructure, use Cal.com instead.


Table of Contents

  1. What Is Cal.diy?
  2. The Four Products at a Glance
  3. Quick Start
  4. Installation
  5. First-Run Setup
  6. Part 1 — Using the Scheduling Web App
  7. Part 2 — Embedding Cal.diy on Your Website
  8. Part 3 — Building With @calcom/atoms (React SDK)
  9. Part 4 — The Platform API v2
  10. Common Tasks & Recipes
  11. Troubleshooting
  12. Frequently Asked Questions
  13. Reference

1. What Is Cal.diy?

Cal.diy is an open-source alternative to scheduling tools like Calendly. You create event types (for example, "30-minute intro call" or "60-minute consultation"), publish your booking page, and other people pick an open slot. Cal.diy checks your connected calendars for conflicts, reserves the slot, creates the meeting (including a video link when configured), and emails both parties.

It is built as a monorepo (one repository containing several independently shipped products) on top of:

  • Next.js + React — the web application and UI
  • tRPC — the internal type-safe API the web app calls
  • Prisma + PostgreSQL — the data layer
  • Tailwind CSS — styling
  • Daily.co — default video conferencing ("Cal Video")

How Cal.diy differs from Cal.com

Cal.diy is a community fork with all "Enterprise Edition" code removed:

Removed from Cal.diy Still present
Teams, Organizations Individual user scheduling
Insights / analytics dashboards Event types & availability
Workflows (automated reminders) Calendar & app integrations
SSO / SAML enterprise login Email / password & Google login
License-key gating Platform API v2 & embed SDKs

Important

Cal.diy is 100% MIT-licensed with no "Open Core" split and no license key required — everything works out of the box. There is no hosted or managed version; you run it on your own infrastructure. Contributions to this repo do not flow back to Cal.com's production platform.

Note

Because this is a fork of a much larger commercial codebase, you will find scaffolding for team- and organization-related features in the source. This guide documents the individual-user scheduling flows, the embed SDKs, the React component library, and the Platform API — the surfaces that work without an enterprise plan.


2. The Four Products at a Glance

Cal.diy is not a single application — it ships four separately consumable products that share one scheduling backend. Pick the one that matches your goal.

Product Package Who it's for What you get
Scheduling web app apps/web Self-hosters, individuals A full booking platform you host: booking pages, event types, availability, integrations
Embed SDK @calcom/embed-core, @calcom/embed-react, @calcom/embed-snippet Website owners Drop your Cal.diy booking page into any site as an inline widget, popup, or floating button
Atoms (React SDK) @calcom/atoms Product developers Pre-built React components (<Booker>, <AvailabilitySettings>, …) to build scheduling into your own app
Platform API v2 apps/api/v2 Backend developers A versioned REST API for bookings, slots, event types, schedules, and calendars

The rest of this guide has one dedicated part per product. Start with Part 1 if you want to run the booking platform; jump to Part 2/3/4 if you are a developer integrating scheduling into your own product.

                         ┌─────────────────────────────┐
   Your website  ──embed─▶│                             │
   Your React app ─atoms─▶│   Cal.diy scheduling core   │◀── booking page (apps/web)
   Your backend  ──API───▶│  (availability, bookings,   │
                          │   calendars, conferencing)  │◀── you (the host) manage here
                          └─────────────────────────────┘

3. Quick Start

The fastest way to see Cal.diy running locally is the yarn dx flow, which spins up a local PostgreSQL database (via Docker) seeded with test users.

Important

yarn dx requires Docker and Docker Compose to be installed and running. It starts a throwaway Postgres instance and logs the test credentials in the console.

# 1. Clone the repository
git clone https://github.com/calcom/cal.diy.git
cd cal.diy

# 2. Install dependencies (Yarn 4 is bundled via .yarnrc.yml)
yarn

# 3. Create your environment file and generate secrets
cp .env.example .env
# Generate a cookie secret and an encryption key:
echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)" >> .env
echo "CALENDSO_ENCRYPTION_KEY=$(openssl rand -base64 24)" >> .env

# 4. Start everything (DB + web app, with seeded users)
yarn dx
✔ Postgres started on localhost:5432
✔ Migrations applied
✔ Seed users created
▲ Next.js ready on http://localhost:3000

Open http://localhost:3000 and sign in with one of the seeded accounts:

Email Password Role
[email removed] free Free user
[email removed] pro Pro user
[email removed] trial Trial user
[email removed] ADMINadmin2022! Admin user
[email removed] onboarding Onboarding incomplete

Tip

To browse the full seeded dataset, run yarn db-studio and open http://localhost:5555 — this launches Prisma Studio, a visual database browser.


4. Installation

Cal.diy supports local development, Docker, and one-click cloud deployments. Choose the path that fits you.

Prerequisites

Requirement Version
Node.js >= 18.x
PostgreSQL >= 13.x
Yarn recommended (bundled, Yarn 4)
Docker + Docker Compose required only for yarn dx and the Docker deployment path

Tip

The build is memory-hungry. Add export NODE_OPTIONS="--max-old-space-size=16384" to your shell (adjust the number to the RAM you can spare) before running the app to avoid out-of-memory crashes during compilation.

Method 1 — Manual setup (local development)

macOS / Linux

git clone https://github.com/calcom/cal.diy.git
cd cal.diy
yarn
cp .env.example .env
# Edit .env: set DATABASE_URL, NEXTAUTH_SECRET, CALENDSO_ENCRYPTION_KEY
nvm install && nvm use          # match the required Node version
yarn workspace @calcom/prisma db-migrate
yarn dev

Windows (Git Bash, admin)

git clone -c core.symlinks=true https://github.com/calcom/cal.diy.git
cd cal.diy
yarn
cp .env.example .env
# Replace the prisma .env symlink with a real copy (avoids a Prisma error):
rm packages/prisma/.env && cp .env packages/prisma/.env
yarn workspace @calcom/prisma db-migrate
yarn dev

Configure your database connection in .env:

DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>/<db-name>'
# Example for a local Postgres:
# DATABASE_URL='postgresql://postgres:postgres@localhost:5432/calendso'

Note

Copy your DATABASE_URL from .env into .env.appStore as well — the app-store build step reads it from there.

After the database is reachable, apply the schema:

# Development (creates/updates a dev database from schema.prisma):
yarn workspace @calcom/prisma db-migrate

# Production (applies committed migrations only):
yarn workspace @calcom/prisma db-deploy

Tip

To view emails Cal.diy sends during development, run MailHog:

docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog

Then open http://localhost:8025. This is required when E2E_TEST_MAILHOG_ENABLED=1.

Method 2 — Docker Compose

The published image lives on Docker Hub at calcom/cal.diy.

git clone --recursive https://github.com/calcom/cal.diy.git
cd cal.diy
cp .env.example .env

# Generate the two required secrets BEFORE first start:
openssl rand -base64 32   # → put in NEXTAUTH_SECRET
openssl rand -base64 24   # → put in CALENDSO_ENCRYPTION_KEY

docker compose pull       # optional: pre-pull images
docker compose up -d      # full stack: Postgres + web app + Prisma Studio

You can also run subsets:

docker compose up -d calcom studio   # app + Prisma Studio against a remote DB
docker compose up -d calcom          # web app only (DATABASE_URL must point to a reachable DB)

Caution

Never ship the default secret placeholder values for NEXTAUTH_SECRET or CALENDSO_ENCRYPTION_KEY to production — they are a serious security risk. CALENDSO_ENCRYPTION_KEY must be 32 bytes (AES-256) and must match between build time and run time.

Note

ARM users: pull the -arm-suffixed image tag, e.g. docker pull calcom/cal.diy:v5.6.19-arm.

To update a running Docker stack:

docker compose down
docker compose pull
docker compose up -d

Method 3 — One-click cloud deployments

Cal.diy ships deploy buttons for several platforms (see the repository README for the live buttons):

Platform Notes
Railway One-click template; PostgreSQL add-on included
Northflank One-click stack deploy
Render Deploy from the calcom/docker repo
Vercel Requires the Pro plan (free plan limits serverless function count)
Heroku app.json provisions heroku-postgresql:mini; runs prisma migrate deploy on deploy
Gitpod Fully configured browser workspace, prebuilds enabled

Warning

When deploying to Vercel, you must provide DATABASE_URL, NEXT_PUBLIC_WEBAPP_URL, NEXTAUTH_URL, NEXTAUTH_SECRET, CRON_API_KEY, and CALENDSO_ENCRYPTION_KEY as environment variables.


5. First-Run Setup

The first time you open Cal.diy at your NEXT_PUBLIC_WEBAPP_URL (default http://localhost:3000), a setup wizard initializes. Define your first user and you're ready to go.

Creating your first user

If you didn't use the seeded yarn dx accounts, create a user one of two ways:

Approach 1 — Prisma Studio (manual):

yarn db-studio
  1. Open the User model and add a record.
  2. Fill in email, username, and password (encrypt the password with BCrypt).
  3. Set metadata to an empty object {}.
  4. Save, then sign in at http://localhost:3000.

Approach 2 — Seed dummy users:

cd packages/prisma
yarn db-seed

Note

New users are placed on a TRIAL plan by default. You can change this default in packages/prisma/schema.prisma.

Tip

If the setup wizard appears to require connecting a calendar but you'd rather skip it, navigate directly to <NEXT_PUBLIC_WEBAPP_URL>/event-types. You can add calendar integrations later under Settings → Integrations.


6. Part 1 — Using the Scheduling Web App

This part covers the day-to-day of running your booking platform: creating event types, setting availability, managing bookings, and connecting integrations.

6.1 The booking lifecycle

You create an Event Type  ─▶  You publish your Booking Page
                                       │
   A guest opens your page  ◀──────────┘
            │
   Picks an open slot (the "Booker")  ─▶  Slot is reserved
            │
   Fills in booking questions  ─▶  Booking is confirmed
            │
   Calendar event + video link created  ─▶  Confirmation emails sent

Note

Cal.diy uses a slot reservation system to prevent double-booking. When you open a booking page, available slots come from a getSchedule query that already excludes slots reserved by others. If two people open the same page and one selects a slot, the other will see a "Slot no longer available" state when they try to confirm. Availability is refreshed on window focus, on a configurable interval (default ~5 minutes), and when a slot is selected.

6.2 Event Types

An event type is a bookable offering. Create and manage them under Event Types in the dashboard. Each event type has its own slug and produces a booking URL like https://your-cal/<username>/<event-slug>.

Event types expose a rich set of options. The most important ones:

Setting What it does
title / description Name and blurb shown on the booking page
slug URL segment for the event (/you/30min)
length Duration in minutes
multipleDuration Offer several selectable durations
locations Where the meeting happens (video app, phone, in person, custom)
hidden Hide the event from your public profile
requiresConfirmation You must approve each booking before it's confirmed
requiresConfirmationThreshold Only require confirmation within X hours/minutes of the start
requiresBookerEmailVerification Booker must verify their email before booking
bookingRequiresAuthentication Booker must be signed in
minimumBookingNotice Minimum lead time before a slot can be booked
beforeEventBuffer / afterEventBuffer Padding before/after each meeting
slotInterval Granularity of offered start times
onlyShowFirstAvailableSlot Show just the next opening
bookingLimits / durationLimits Cap bookings per day/week/month/year
periodType / periodDays How far into the future people can book (rolling or fixed range)
seatsPerTimeSlot Turn the event into a group/seated event
seatsShowAttendees / seatsShowAvailabilityCount Reveal who's attending / seats left
recurringEvent Make the event repeat (daily/weekly/monthly/yearly)
price / currency Charge for the booking (requires a payment app)
successRedirectUrl Send bookers to your own page after booking
disableGuests Prevent bookers from adding extra guests
disableCancelling / disableRescheduling Lock the booking after creation
requiresCancellationReason Force a reason when cancelling
hideCalendarNotes / hideCalendarEventDetails / hideOrganizerEmail Privacy controls on the calendar event
lockTimeZoneToggleOnBookingPage / lockedTimeZone Pin the booking page to one time zone
eventTypeColor Custom light/dark accent colors
bookerLayouts Which calendar layouts the booker can use (see below)

Tip

Booker layouts control how the date/time picker is presented. Three are available: month_view (default), week_view, and column_view. Set the enabled layouts and a default per event type, or globally in your profile.

Recurring events

Set recurringEvent to make an event repeat. The frequency uses standard recurrence rules:

Frequency Value
Yearly YEARLY
Monthly MONTHLY
Weekly WEEKLY
Daily DAILY

You specify an interval (e.g. every 2 weeks) and a count (how many occurrences).

Booking questions (the form builder)

Each event type can collect custom information through booking questions. The form builder supports these field types:

Type Use
name Booker name (first/last or single field)
email Booker email
phone Phone number
text / textarea Short / long free text
number Numeric input
address Postal address
multiemail Multiple email addresses
select / multiselect Single / multiple choice from a list
checkbox / radio Boolean groups / single choice
radioInput A choice that reveals a follow-up input (e.g. pick "Phone" → show a phone field)
boolean A single yes/no toggle
url A URL field

Fields can be marked required, hidden, given placeholders, and constrained with minLength/maxLength. You can also restrict bookers by email domain using require/exclude email rules (e.g. only allow @yourcompany.com).

Note

System fields (like name and email) cannot be deleted, but depending on their editable setting they can be hidden or made optional. Fully custom fields you add are completely editable.

6.3 Availability & Schedules

Under Availability, you define schedules — named sets of working hours and time zone (e.g. "Working Hours", "Weekend"). Assign a schedule to one or more event types via scheduleId.

Within a schedule you set:

  • Weekly availability (which days and time ranges you're bookable)
  • Date overrides (specific dates with different or no availability)
  • The schedule's time zone

Tip

Cal.diy computes real availability by subtracting busy times (from connected calendars), existing bookings, buffers, and reserved slots from your schedule. The booking page and the actual booking call share the same availability logic so a slot shown as open will still be open when confirmed (barring a race with another booker).

6.4 The Booker (booking page)

Your public booking page is what guests use. It shows your event details, a calendar in the selected layout, open time slots, and the booking questions form. After a successful booking the guest sees a confirmation (or your successRedirectUrl).

The booking page URL pattern is:

https://<your-cal-domain>/<username>/<event-slug>

6.5 Managing bookings

Under Bookings you can see upcoming, past, cancelled, and unconfirmed bookings, and take action on each:

  • Confirm / reject bookings that require confirmation
  • Reschedule — move a booking to a new time (subject to minimumRescheduleNotice and reschedule locks)
  • Cancel — optionally requiring a cancellation reason; you can cancel a single booking, all remaining bookings in a series, or subsequent bookings
  • Add/remove attendees and guests
  • Mark no-show for host or attendees
  • Reassign a booking to a different host (round-robin events)

Note

Every booking action — created, rescheduled, accepted, cancelled, rejected, attendees changed, no-show updates, seat changes — is recorded in an immutable booking audit trail. This is invisible to bookers but gives you a complete, tamper-proof history. See the Admin Guide for details.

Seated (group) events

Setting seatsPerTimeSlot turns an event into a group booking where multiple guests share one time slot. Each attendee gets a seat reference. You control whether attendees can see each other (seatsShowAttendees) and whether remaining seat counts are shown (seatsShowAvailabilityCount). When a seated slot is full you'll see "No more seats left at this seated booking."

6.6 Connecting calendars

Connect your calendars under Settings → Integrations (or Apps). Connected calendars are used for two things: checking your busy times (so you're never double-booked) and writing new bookings to a chosen destination calendar.

Supported calendar integrations include Google Calendar, Office 365 / Outlook, Apple Calendar (CalDAV), CalDAV servers, Exchange (2013/2016/EWS), Lark, Feishu, Zoho Calendar, and ICS feeds. Each requires the host operator to configure OAuth credentials (see your administrator or the Admin Guide).

Tip

You can connect multiple calendars for conflict-checking but pick a single destination calendar where new Cal.diy events are written.

6.7 Conferencing (video meetings)

By default, video meetings use Cal Video (powered by Daily.co). You can change the default conferencing app per user or per event type. Supported conferencing apps include Cal Video / Daily, Zoom, Google Meet, Microsoft Teams, Jitsi, Whereby, Around, Element Call, Huddle01, Mirotalk, Riverside, Salesroom, Sylaps, Tandem, Webex, and more.

Note

Setting an event's location to a video app means the meeting URL is generated automatically when the booking is confirmed and included in the calendar invite and emails.

6.8 Payments

Charge for bookings by setting an event type's price and currency and connecting a payment app. Supported payment integrations include Stripe, PayPal, HitPay, BTCPay Server, Alby (Lightning), and a built-in mock payment app for testing.

Note

When a paid event is booked, the booking enters an awaiting-payment state until the charge succeeds; an unpaid booking is excluded from your calendar until completed.

6.9 Other integrations & apps

Cal.diy ships a large app store of integrations beyond calendars, conferencing, and payments:

Category Examples
CRM Salesforce, HubSpot, Pipedrive, Zoho CRM, Zoho Bigin, Close, Attio
Analytics Google Analytics (GA4), Google Tag Manager, Plausible, Matomo, PostHog, Umami, Fathom, Meta Pixel, Databuddy
Automation Zapier, Make, n8n, Pipedream
Messaging / reminders WhatsApp (Twilio), Telegram, Signal, Discord, Basecamp, Intercom
Other Giphy (thank-you pages), QR code, Weather in your calendar, Vital (health)

Tip

Most third-party apps require credentials that the host operator configures via environment variables. See Integrations setup in the Admin Guide (Google, Microsoft, Zoom, Daily, Basecamp, HubSpot, Zoho, Pipedrive, etc.).


7. Part 2 — Embedding Cal.diy on Your Website

The embed SDK lets you place your Cal.diy booking flow on any website — as an inline widget, a popup triggered by a click, a floating button, or a modal. There are three packages:

Package Use
@calcom/embed-snippet Vanilla JS loader snippet (no framework)
@calcom/embed-core The embed engine (loaded by the snippet)
@calcom/embed-react React wrapper: a <Cal> component + getCalApi() hook

All embeds talk to a booking page identified by a cal link (e.g. pro for your-cal/pro) served from your Cal.diy instance's /embed/embed.js.

7.1 Vanilla JS

The snippet fetches embed.js and exposes a global Cal(...) function. Conceptually:

<!-- Load the embed snippet (points at YOUR Cal.diy instance) -->
<script>
  (function (C, A, L) { /* … embed snippet … */ })(window, "https://your-cal/embed/embed.js", "init");
  Cal("init", { origin: "https://your-cal" });

  // Inline embed into a div
  Cal("inline", {
    elementOrSelector: "#my-cal-inline",
    calLink: "pro",
    config: { layout: "month_view", theme: "dark" },
  });

  // Customize the UI
  Cal("ui", {
    styles: { branding: { brandColor: "#000000" } },
    hideEventTypeDetails: false,
  });
</script>
<div id="my-cal-inline" style="width:100%;height:100%;overflow:scroll"></div>

You can also drive popups declaratively with data attributes on any element:

<button
  data-cal-link="pro"
  data-cal-namespace="my-popup"
  data-cal-config='{"layout":"month_view","theme":"dark"}'>
  Book a call
</button>

7.2 React (@calcom/embed-react)

Install the package and use the <Cal> component for inline embeds, or getCalApi() to drive popups, floating buttons, and events.

Inline embed:

import Cal, { getCalApi, type EmbedEvent } from "@calcom/embed-react";
import { useEffect } from "react";

export default function BookingWidget() {
  useEffect(() => {
    (async () => {
      const cal = await getCalApi({ namespace: "inline" });
      cal("ui", {
        cssVarsPerTheme: {
          light: { "cal-border-booker": "red" },
          dark:  { "cal-border-booker": "red" },
        },
      });
    })();
  }, []);

  return (
    <Cal
      namespace="inline"
      calOrigin="https://your-cal"
      embedJsUrl="https://your-cal/embed/embed.js"
      calLink="pro"
      style={{ width: "100%", height: "100%", overflow: "scroll" }}
      config={{
        name: "John Doe",
        email: "[email removed]",
        notes: "Intro meeting",
        guests: ["[email removed]"],
        theme: "dark",
      }}
    />
  );
}

Floating button:

const cal = await getCalApi({ namespace: "floating", embedJsUrl: "https://your-cal/embed/embed.js" });
cal("floatingButton", {
  calLink: "pro",
  calOrigin: "https://your-cal",
  config: { theme: "dark" },
});

Popup on element click:

const cal = await getCalApi({ namespace: "element-click" });
cal("ui", { styles: { branding: { brandColor: "#000000" } }, hideEventTypeDetails: false });
// then add data-cal-namespace="element-click" data-cal-link="pro" to a button

7.3 The embed API surface

The cal(...) function (returned by getCalApi(), or the global Cal) accepts these instructions:

Instruction Purpose
"init" Initialize the embed and set the origin of your Cal.diy instance
"inline" Render the booker inline into an element
"floatingButton" Add a floating "Book" button
"modal" / popup Open the booker in an overlay (also via data attributes)
"ui" Style the booker (brand color, CSS variables per theme, hide event details)
"preload" Pre-fetch a booking page for instant opening
"on" / "off" Subscribe / unsubscribe to embed events

Embed config options (passed via config or data-cal-config):

Option Example
layout month_view, week_view, column_view
theme dark, light
name, email, notes, guests Prefill the booking form
useSlotsViewOnSmallScreen "true" to force the slots list on mobile

7.4 Embed events

Subscribe to lifecycle events to react in your own app (analytics, redirects, etc.):

const cal = await getCalApi({ namespace: "inline" });
cal("on", { action: "*", callback: (e) => console.log(e.detail) });

cal("on", {
  action: "bookingSuccessfulV2",
  callback: (e: EmbedEvent<"bookingSuccessfulV2">) => {
    const { startTime, endTime, title } = e.detail.data;
    console.log("Booked:", title, startTime, endTime);
  },
});
Event Fires when
linkReady The embed iframe has loaded the booking page
bookerReady The booker UI is interactive (gives eventId, eventSlug)
bookingSuccessful / bookingSuccessfulV2 A booking completes (V2 includes startTime, endTime, title)
* Wildcard — receive all events

Tip

Always pair an cal("on", …) with a matching cal("off", …) (for example in a React useEffect cleanup) to avoid duplicate handlers across re-renders.


8. Part 3 — Building With @calcom/atoms (React SDK)

@calcom/atoms is a library of customizable React UI components that let you build a full scheduling experience inside your own product — without iframes. Where the embed SDK drops in a hosted booking page, atoms give you native components you compose and style.

Note

Atoms are designed for the Platform model: your app authenticates as a Cal.diy OAuth client and manages "managed users" through the Platform API v2. Atoms talk to the API at NEXT_PUBLIC_CALCOM_API_URL (e.g. http://localhost:5555/api/v2). See Part 4 for the API and the Admin Guide for OAuth-client setup.

8.1 Provider

Wrap your app in a provider. Use CalProvider for a platform OAuth client, or CalOAuthProvider for an OAuth 2.0 client flow:

import { CalProvider } from "@calcom/atoms";

<CalProvider
  clientId={process.env.NEXT_PUBLIC_X_CAL_ID}
  options={{ apiUrl: "http://localhost:5555/api/v2" }}
  accessToken={managedUserAccessToken}
>
  {/* your scheduling UI */}
</CalProvider>

8.2 Components

The package exports ready-made components for every part of scheduling:

Component What it renders
<Booker> The full booking flow (pick a slot, fill the form, book)
<BookerEmbed> Booker pre-configured for embedding
<AvailabilitySettings> Edit a schedule's working hours
<CreateSchedule> / <ListSchedules> Create / list availability schedules
<ListEventTypes> / <CreateEventType> / <EventTypeSettings> Manage event types
<CalendarSettings> / <CalendarView> Calendar configuration and views
<SelectedCalendarsSettings> / <DestinationCalendarSettings> Choose conflict-check and destination calendars
<ConferencingAppsSettings> Manage video apps
<Connect.*>, <GcalConnect>, <OutlookConnect>, <StripeConnect> Connect Google / Outlook / Stripe
<TroubleShooter> Diagnose why slots aren't showing

8.3 Hooks

Atoms also expose hooks for data access and mutations:

Hook Returns / does
useMe() The current managed user
useIsPlatform() Whether you're running in platform mode
useAvailableSlots() Open slots for an event type
useBooking() / useBookings() A single / list of bookings
useCancelBooking() Cancel a booking
useEventTypes() / useEventType() (useEvent) List / get event types
useCreateEventType() / useEventTypeById() Create / fetch an event type
useConnectedCalendars() / useAddSelectedCalendar() / useRemoveSelectedCalendar() Manage connected calendars
useDeleteCalendarCredentials() Disconnect a calendar
useAtomsContext() Access provider context

Important

Atoms ship their own CSS scoped under .calcom-atoms (so they don't collide with your app's styles). Import the package's stylesheet and ensure your build externalizes react / react-dom. The webAppUrl global config (defaults to https://cal.com) tells the Booker which backend to fetch from.


9. Part 4 — The Platform API v2

The Platform API (apps/api/v2) is a versioned REST API built on NestJS for programmatically managing bookings, slots, event types, schedules, and calendars. It is the backend that powers the atoms SDK and any custom integration you build.

9.1 Base URL and health

By default the API runs on port 5555 and is served under the /v2 prefix (with /api/v2 rewritten to /v2):

# Health check (no auth):
curl http://localhost:5555/health
# → OK

9.2 Authentication

The API accepts three authentication modes:

Mode How Use case
API key Authorization: Bearer cal_xxx Server-to-server, single account
OAuth client Headers x-cal-client-id + x-cal-secret-key Platform apps managing many users
OAuth 2.0 access token Authorization: Bearer <access_token> Delegated user access via the authorize/token flow
# Fetch the authenticated user with an API key:
curl http://localhost:5555/v2/me \
  -H "Authorization: Bearer cal_live_xxxxxxxx"
{ "status": "success", "data": { "id": 1, "email": "[email removed]", "username": "you" } }

9.3 Versioning

The API uses custom header-based versioning. Send the version with the cal-api-version header; if omitted, it defaults to 2024-04-15.

curl http://localhost:5555/v2/bookings \
  -H "Authorization: Bearer cal_live_xxx" \
  -H "cal-api-version: 2024-08-13"

Different resources have their own dated versions, for example:

Resource Versions
Bookings 2024-04-15, 2024-08-13
Event types 2024-04-15, 2024-06-14
Schedules 2024-04-15, 2024-06-11
Slots 2024-04-15, 2024-09-04

9.4 Endpoint groups

The API surface is organized into modules. The most useful for building scheduling:

Group What it does
/v2/me The authenticated user
/v2/bookings Create, get, list, reschedule, cancel, mark no-show, reassign, manage attendees/guests/seats
/v2/slots Query available slots; reserve and release slots
/v2/event-types Manage event types
/v2/schedules Manage availability schedules
/v2/calendars Connected calendars, busy times
/v2/selected-calendars / /v2/destination-calendars Conflict-check and write targets
/v2/conferencing Video app connections
/v2/webhooks Register webhooks for booking events
/v2/oauth-clients / /v2/auth/oauth2/* Platform OAuth clients and the OAuth 2.0 flow
/v2/api-keys Manage API keys
/v2/timezones Time zone helpers

Tip

In development the API generates an OpenAPI/Swagger document at startup, so you can explore the full, exact request/response schemas interactively.

9.5 Rate limits

Every authenticated response carries rate-limit headers, and exceeding a limit returns HTTP 429:

x-ratelimit-limit-default: 120
x-ratelimit-remaining-default: 118
x-ratelimit-reset-default: 42

Limits can be the default or custom per API key, and multiple windows (e.g. a short and a long limit) can apply simultaneously. When blocked, wait for the x-ratelimit-reset-* window (seconds) before retrying.


10. Common Tasks & Recipes

Reset your local database and reseed

cd packages/prisma
yarn db-seed       # populate with dummy users
# or, for a full reset (development only):
yarn workspace @calcom/prisma db-reset

Control log verbosity

Set NEXT_PUBLIC_LOGGER_LEVEL in .env to control how much Cal.diy logs (for all tRPC queries/mutations):

echo 'NEXT_PUBLIC_LOGGER_LEVEL=3' >> .env   # info and above
Level Meaning
0 silly

The logger includes the chosen level and higher.

Upgrade to a new version

git pull
yarn                                         # pick up dependency changes
yarn workspace @calcom/prisma db-deploy      # apply migrations (production)
yarn predev                                  # check for .env changes
yarn build && yarn start                     # production build

Caution

In a development environment, db-migrate can reset your local database. Use db-deploy in production.

Add Google Calendar to your App Store

After your administrator configures GOOGLE_API_CREDENTIALS, repopulate the app store:

cd packages/prisma
yarn seed-app-store

Enable rate limiting (optional, via Unkey)

Cal.diy can use Unkey for rate limiting. Create a root key with ratelimit.create_namespace and ratelimit.limit permissions and set UNKEY_ROOT_KEY in .env. Without it, Cal.diy runs normally without rate limiting.


11. Troubleshooting

Error: No key set vapidDetails.publicKey

Cause: Web Push (VAPID) keys are missing. Solution: Generate them and add to .env:

npx web-push generate-vapid-keys
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your_public_key_here
VAPID_PRIVATE_KEY=your_private_key_here

Prevention: Set these before first run; never commit real keys to .env.example.

Error: [next-auth][error][CLIENT_FETCH_ERROR]

Cause: In Docker, the container can't resolve its own WEBAPP_URL for the auth callback (it doesn't share your machine's DNS). Solution: Set NEXTAUTH_URL so the backend loops back to itself:

NEXTAUTH_URL=http://localhost:3000/api/auth

Error: Invalid 'prisma.user.create()' when creating a user

Cause: The metadata field is empty. Solution: Use an empty JSON object {} for metadata, and leave id empty so it autoincrements.

Requests rejected behind a load balancer (SSL edge termination)

Cause: Your load balancer terminates SSL and the app rejects the upstream certificate. Solution (only if you trust the LB): Set NODE_TLS_REJECT_UNAUTHORIZED=0.

E2E test browsers not installed

Error: Executable doesn't exist at .../ms-playwright/chromium-.../Chromium Solution: npx playwright install

Prisma error on Windows: unexpected character / in variable name

Cause: The packages/prisma/.env symlink isn't followed on Windows. Solution: Replace the symlink with a real copy:

rm packages/prisma/.env && cp .env packages/prisma/.env

"Slot no longer available" when confirming a booking

Cause: Another booker reserved or booked that slot between when you loaded the page and when you clicked confirm. Solution: Pick a different slot. The page re-fetches availability on focus and on a timer; refresh to see the latest openings.

API returns 429 Too Many Requests

Cause: You exceeded a rate-limit window. Solution: Read the x-ratelimit-reset-* header (seconds until reset) and back off until then. Consider requesting a higher custom limit on your API key.

Booking API errors (Platform API v2)

Common, exactly-worded responses you may hit:

Message Meaning
Event Type not found / Event Type with ID=… not found The eventTypeId is wrong or hidden
User with username … not found Bad username in the request
Invalid start date / Invalid end date Malformed or out-of-range date
Attempting to book a meeting in the past. Start time is before now
User either already has booking at this time or is not available Slot conflicts with an existing booking/busy time
No more seats left at this seated booking. Seated event is full
Invalid Access Token, not present in Redis or DB Expired/invalid OAuth access token

12. Frequently Asked Questions

Q: Is Cal.diy the same as Cal.com? No. Cal.diy is the community fork of Cal.com with all enterprise/commercial features removed (Teams, Organizations, Insights, Workflows, SSO/SAML) and no license key. It's 100% MIT-licensed and self-hosted only.

Q: Is there a hosted version of Cal.diy I can sign up for? No. Cal.diy has no managed/hosted offering — you run it on your own infrastructure. For a hosted, commercial product use Cal.com.

Q: Do I need a license key? No. Everything works out of the box without any Cal.com account or license.

Q: What database does it use? PostgreSQL (>= 13). The schema is managed by Prisma in packages/prisma/schema.prisma.

Q: How do I get video meetings working? Cal Video (Daily.co) is the default. Set DAILY_API_KEY (your administrator does this) or connect another video app like Zoom or Google Meet per event type.

Q: Can I charge for bookings? Yes. Set an event type's price and currency and connect a payment app (Stripe, PayPal, HitPay, BTCPay, Alby).

Q: Can I embed the booking page in my site without React? Yes. Use @calcom/embed-snippet (vanilla JS) with data-cal-link attributes or the global Cal(...) function.

Q: What's the difference between the embed SDK and atoms? The embed SDK drops a hosted booking page into your site via an iframe. Atoms are native React components you compose and style directly, backed by the Platform API.

Q: How do I prefill the booking form? Pass name, email, notes, and guests in the embed config (or as data-cal-config), or as query params on the booking URL.

Q: How far in advance can people book? Controlled per event type via periodType, periodDays, or a fixed periodStartDate/periodEndDate range, plus minimumBookingNotice for the near end.

Q: Why is a slot I expect to be open not showing? It may conflict with a connected calendar's busy time, an existing booking, a buffer (beforeEventBuffer/afterEventBuffer), minimumBookingNotice, a booking limit, or a slot reserved by another booker. Use the <TroubleShooter> atom or check your schedule.

Q: Can I restrict who can book by email? Yes — booking-question fields support require/exclude email-domain rules, and you can require email verification or authentication on an event type.

Q: How do I run it on ARM (Apple Silicon, ARM servers)? Pull the -arm Docker image tag, e.g. calcom/cal.diy:v5.6.19-arm.

Q: Where do bookings get written? To the destination calendar you select. Other connected calendars are used only for conflict checking.

Q: How do I see emails during development? Run MailHog (docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog) and open http://localhost:8025.

Q: Is there an API to build my own scheduling UI? Yes — the Platform API v2 (REST, NestJS) plus the @calcom/atoms React components. See Parts 3 and 4.


13. Reference

Key commands

Command What it does
yarn Install dependencies (Yarn 4)
yarn dx Start DB + app with seeded users (needs Docker)
yarn dev Start the dev server
yarn build / yarn start Production build / start
yarn db-studio Open Prisma Studio (DB browser)
yarn workspace @calcom/prisma db-migrate Apply schema (development)
yarn workspace @calcom/prisma db-deploy Apply migrations (production)
cd packages/prisma && yarn db-seed Seed dummy users
cd packages/prisma && yarn seed-app-store Repopulate the app store
yarn test-e2e Run Playwright end-to-end tests

Default ports

Port Service
3000 Web app
5555 Prisma Studio / Platform API
1025 / 8025 MailHog SMTP / web UI

Essential environment variables (user-facing)

Variable Purpose
DATABASE_URL PostgreSQL connection string
NEXT_PUBLIC_WEBAPP_URL Public base URL of your instance
NEXTAUTH_URL Auth server URL (defaults to {WEBAPP_URL}/api/auth)
NEXTAUTH_SECRET Cookie encryption key (openssl rand -base64 32)
CALENDSO_ENCRYPTION_KEY App encryption key, 32 bytes (openssl rand -base64 24)
NEXT_PUBLIC_VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY Web Push keys
NEXT_PUBLIC_LOGGER_LEVEL Log verbosity (0–6)

The complete, grouped environment-variable reference (integrations, Stripe, email, video, rate limits, the Platform API) is in the Admin Guide.

Booker layouts

Layout Value
Month view (default) month_view
Week view week_view
Column view column_view

Where to get help