Skip to content
OpenSmartRoute
Skillv1.0.0

cal-com

Expert guidance for Cal.com, the open-source scheduling platform for building booking and appointment systems. Helps developers integrate Cal.com's embed widgets, REST API, and webhooks to add schedul

by terminalskills(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from terminalskills/skills (skills/cal-com/SKILL.md). Install upstream with npx skills add terminalskills/skills --skill cal-com. Copyright stays with the author (Apache-2.0).

Cal.com — Scheduling Infrastructure

Overview

Cal.com, the open-source scheduling platform for building booking and appointment systems. Helps developers integrate Cal.com's embed widgets, REST API, and webhooks to add scheduling capabilities to their applications.

Instructions

Embed Widget

Add a booking widget to any website:

// src/components/BookingWidget.tsx — Embed Cal.com scheduling in a React app
import Cal, { getCalApi } from "@calcom/embed-react";
import { useEffect } from "react";

export function BookingWidget() {
  useEffect(() => {
    (async () => {
      const cal = await getCalApi();
      // Configure the embed appearance
      cal("ui", {
        theme: "light",
        styles: { branding: { brandColor: "#6366f1" } },
        hideEventTypeDetails: false,
        layout: "month_view",       // "month_view" | "week_view" | "column_view"
      });
    })();
  }, []);

  return (
    <Cal
      calLink="your-team/discovery-call"   // Your Cal.com event link
      style={{ width: "100%", height: "100%", overflow: "scroll" }}
      config={{
        layout: "month_view",
        name: "Customer Name",              // Pre-fill guest name
        email: "customer@example.com",      // Pre-fill guest email
        notes: "Interested in Enterprise plan",
      }}
    />
  );
}

// Floating button that opens calendar in a popup
export function BookingButton() {
  useEffect(() => {
    (async () => {
      const cal = await getCalApi();
      cal("floatingButton", {
        calLink: "your-team/discovery-call",
        buttonText: "Book a Demo",
        buttonColor: "#6366f1",
        buttonTextColor: "#ffffff",
        buttonPosition: "bottom-right",
      });
    })();
  }, []);

  return null;   // The floating button renders itself
}

REST API Integration

Manage bookings, event types, and availability programmatically:

// src/cal/client.ts — Cal.com API client for server-side operations
const CAL_API_URL = "https://api.cal.com/v2";
const CAL_API_KEY = process.env.CAL_API_KEY!;

async function calFetch(path: string, options?: RequestInit) {
  const response = await fetch(`${CAL_API_URL}${path}`, {
    ...options,
    headers: {
      "Content-Type": "application/json",
      "cal-api-version": "2024-08-13",       // Pin API version for stability
      Authorization: `Bearer ${CAL_API_KEY}`,
      ...options?.headers,
    },
  });
  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Cal.com API error: ${error.message}`);
  }
  return response.json();
}

// Create a new event type (e.g., "30-min Discovery Call")
async function createEventType(params: {
  title: string;
  slug: string;
  lengthInMinutes: number;
  description?: string;
  locations?: { type: string; link?: string }[];
}) {
  return calFetch("/event-types", {
    method: "POST",
    body: JSON.stringify({
      title: params.title,
      slug: params.slug,
      lengthInMinutes: params.lengthInMinutes,
      description: params.description,
      locations: params.locations ?? [
        { type: "integrations:google:meet" },  // Default: Google Meet
      ],
      bookingFields: [
        { name: "company", type: "text", label: "Company Name", required: true },
        { name: "teamSize", type: "select", label: "Team Size",
          options: ["1-10", "11-50", "51-200", "200+"], required: true },
      ],
    }),
  });
}

// Get available time slots for a date range
async function getAvailability(params: {
  eventTypeId: number;
  startTime: string;   // ISO 8601
  endTime: string;
  timeZone: string;
}) {
  const query = new URLSearchParams({
    eventTypeId: String(params.eventTypeId),
    startTime: params.startTime,
    endTime: params.endTime,
    timeZone: params.timeZone,
  });
  return calFetch(`/slots?${query}`);
}

// Create a booking
async function createBooking(params: {
  eventTypeId: number;
  start: string;         // ISO 8601 datetime
  attendee: { name: string; email: string; timeZone: string };
  metadata?: Record<string, string>;
}) {
  return calFetch("/bookings", {
    method: "POST",
    body: JSON.stringify({
      eventTypeId: params.eventTypeId,
      start: params.start,
      attendee: params.attendee,
      metadata: params.metadata,
      language: "en",
    }),
  });
}

// Cancel a booking
async function cancelBooking(bookingUid: string, reason?: string) {
  return calFetch(`/bookings/${bookingUid}/cancel`, {
    method: "POST",
    body: JSON.stringify({ reason }),
  });
}

// Reschedule a booking
async function rescheduleBooking(bookingUid: string, newStart: string) {
  return calFetch(`/bookings/${bookingUid}/reschedule`, {
    method: "POST",
    body: JSON.stringify({ start: newStart }),
  });
}

Webhooks

React to booking events in real time:

// app/api/cal-webhook/route.ts — Handle Cal.com webhook events
import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";

const WEBHOOK_SECRET = process.env.CAL_WEBHOOK_SECRET!;

// Verify the webhook signature to ensure it's from Cal.com
function verifySignature(payload: string, signature: string): boolean {
  const expected = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

export async function POST(request: NextRequest) {
  const payload = await request.text();
  const signature = request.headers.get("x-cal-signature-256") ?? "";

  if (!verifySignature(payload, signature)) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  const event = JSON.parse(payload);

  switch (event.triggerEvent) {
    case "BOOKING_CREATED":
      // New booking — send confirmation, update CRM, notify sales
      await handleNewBooking({
        bookingId: event.payload.bookingId,
        attendeeName: event.payload.attendees[0]?.name,
        attendeeEmail: event.payload.attendees[0]?.email,
        startTime: event.payload.startTime,
        eventType: event.payload.eventTitle,
        metadata: event.payload.metadata,
      });
      break;

    case "BOOKING_CANCELLED":
      // Booking cancelled — update CRM, free up resources
      await handleCancellation({
        bookingId: event.payload.bookingId,
        reason: event.payload.cancellationReason,
      });
      break;

    case "BOOKING_RESCHEDULED":
      // Booking moved — update calendar integrations
      await handleReschedule({
        bookingId: event.payload.bookingId,
        oldTime: event.payload.previousStartTime,
        newTime: event.payload.startTime,
      });
      break;

    case "MEETING_ENDED":
      // Meeting finished — trigger follow-up sequence
      await triggerFollowUp(event.payload.bookingId);
      break;
  }

  return NextResponse.json({ received: true });
}

async function handleNewBooking(booking: any) {
  // Example: Create a deal in your CRM
  await crm.createDeal({
    name: `Demo: ${booking.attendeeName}`,
    contact: booking.attendeeEmail,
    stage: "discovery",
    source: "website-booking",
    metadata: booking.metadata,
  });

  // Notify the sales team via Slack
  await slack.postMessage({
    channel: "#sales-notifications",
    text: `📅 New ${booking.eventType} booked!\n👤 ${booking.attendeeName} (${booking.attendeeEmail})\n🕐 ${new Date(booking.startTime).toLocaleString()}`,
  });
}

Managing Team Availability

Configure round-robin scheduling and team calendars:

// src/cal/team-setup.ts — Configure team scheduling rules
async function setupTeamScheduling() {
  // Create a team event type with round-robin assignment
  const eventType = await calFetch("/event-types", {
    method: "POST",
    body: JSON.stringify({
      title: "Product Demo",
      slug: "product-demo",
      lengthInMinutes: 45,
      schedulingType: "ROUND_ROBIN",     // Rotate among team members
      hosts: [
        { userId: 101, isFixed: false },  // Participates in rotation
        { userId: 102, isFixed: false },
        { userId: 103, isFixed: true },   // Always included (e.g., sales engineer)
      ],
      beforeEventBuffer: 15,              // 15-min buffer before meetings
      afterEventBuffer: 10,               // 10-min buffer after
      minimumBookingNotice: 120,          // Minimum 2 hours notice
      slotInterval: 15,                   // Show slots every 15 minutes
      locations: [
        { type: "integrations:zoom" },    // Auto-create Zoom meeting
      ],
    }),
  });

  // Set working hours for the team
  await calFetch("/schedules", {
    method: "POST",
    body: JSON.stringify({
      name: "Business Hours",
      timeZone: "America/New_York",
      isDefault: true,
      availability: [
        // Monday-Friday 9 AM to 5 PM
        { days: [1, 2, 3, 4, 5], startTime: "09:00", endTime: "17:00" },
      ],
      // Block specific dates (holidays, company events)
      dateOverrides: [
        { date: "2026-12-25", startTime: null, endTime: null },  // Christmas — unavailable
        { date: "2026-12-31", startTime: "09:00", endTime: "12:00" },  // NYE — morning only
      ],
    }),
  });
}

Installation

# React embed
npm install @calcom/embed-react

# For self-hosted Cal.com (Docker)
git clone https://github.com/calcom/cal.com.git
cd cal.com
cp .env.example .env
docker compose up -d

Examples

Example 1: Setting up Cal Com with a custom configuration

User request:

I just installed Cal Com. Help me configure it for my TypeScript + React workflow with my preferred keybindings.

The agent creates the configuration file with TypeScript-aware settings, configures relevant plugins/extensions for React development, sets up keyboard shortcuts matching the user's preferences, and verifies the setup works correctly.

Example 2: Extending Cal Com with custom functionality

User request:

I want to add a custom rest api integration to Cal Com. How do I build one?

The agent scaffolds the extension/plugin project, implements the core functionality following Cal Com's API patterns, adds configuration options, and provides testing instructions to verify it works end-to-end.

Guidelines

  1. Use embed for public booking — The React embed handles time zones, availability, and confirmations automatically
  2. Pin API versions — Include cal-api-version header to avoid breaking changes when Cal.com updates
  3. Verify webhook signatures — Always validate x-cal-signature-256 to prevent spoofed events
  4. Buffer time between meetings — Set beforeEventBuffer and afterEventBuffer to prevent back-to-back meetings
  5. Pre-fill known data — Pass name, email, and context via embed config to reduce friction for returning users
  6. Use metadata for attribution — Pass UTM params, plan interest, or referral codes through booking metadata
  7. Handle all webhook events — Don't just handle BOOKING_CREATED; cancellations and reschedules need cleanup too
  8. Self-host for compliance — If you need data residency (GDPR, HIPAA), self-host Cal.com with Docker

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/terminalskills-skills-cal-com/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

terminalskills-skills-cal-com.ocm.jsonjson
{
  "ocm": "1",
  "id": "terminalskills-skills-cal-com",
  "kind": "skill",
  "name": "cal-com",
  "description": "Expert guidance for Cal.com, the open-source scheduling platform for building booking and appointment systems. Helps developers integrate Cal.com's embed widgets, REST API, and webhooks to add scheduling capabilities to their applications.",
  "publisher": "terminalskills",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding",
      "travel"
    ],
    "tags": [
      "skill-md",
      "scheduling",
      "calendar",
      "booking",
      "appointments",
      "api",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Expert guidance for Cal.com, the open-source scheduling platform for building booking and appointment systems. Helps developers integrate Cal.com's embed widgets, REST API, and webhooks to add scheduling capabilities to their applications."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/terminalskills/skills",
      "path": "skills/cal-com/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/terminalskills/skills/blob/HEAD/skills/cal-com/SKILL.md",
      "key": "terminalskills/skills/skills/cal-com/SKILL.md"
    },
    "compatibility": "No special requirements",
    "license": "Apache-2.0"
  },
  "instructions": "# Cal.com — Scheduling Infrastructure\n\n\n## Overview\n\n\nCal.com, the open-source scheduling platform for building booking and appointment systems. Helps developers integrate Cal.com's embed widgets, REST API, and webhooks to add scheduling capabilities to their applications.\n\n\n## Instructions\n\n### Embed Widget\n\nAdd a booking widget to any website:\n\n```tsx\n// src/components/BookingWidget.tsx — Embed Cal.com scheduling in a React app\nimport Cal, { getCalApi } from \"@calcom/embed-react\";\nimport { useEffect } from \"react\";\n\nexport function BookingWidget() {\n  useEffect(() => {\n    (async () => {\n   ",
  "cost": {
    "context_tokens": 2791
  }
}

Fetch it by URL: GET /api/v1/registry/terminalskills-skills-cal-com/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.