.claude/skills/ts-cal-com/SKILL.md
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.
npx skillsauth add eliferjunior/Claude cal-comInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
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: "[email protected]", // 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
}
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 }),
});
}
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()}`,
});
}
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
],
}),
});
}
# 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
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.
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.
cal-api-version header to avoid breaking changes when Cal.com updatesx-cal-signature-256 to prevent spoofed eventsbeforeEventBuffer and afterEventBuffer to prevent back-to-back meetingsdevelopment
Expert guidance for Fireworks AI, the platform for running open-source LLMs (Llama, Mixtral, Qwen, etc.) with enterprise-grade speed and reliability. Helps developers integrate Fireworks' inference API, fine-tune models, and deploy custom model endpoints with function calling and structured output support.
development
Convert any website into clean, structured data with Firecrawl — API-first web scraping service. Use when someone asks to "turn a website into markdown", "scrape website for LLM", "Firecrawl", "extract website content as clean text", "crawl and convert to structured data", or "scrape website for RAG". Covers single-page scraping, full-site crawling, structured extraction, and LLM-ready output.
tools
Expert guidance for Firebase, Google's platform for building and scaling web and mobile applications. Helps developers set up authentication, Firestore/Realtime Database, Cloud Functions, hosting, storage, and analytics using Firebase's SDK and CLI.
development
When the user needs to build file upload functionality for a web application. Use when the user mentions "file upload," "image upload," "upload endpoint," "multipart upload," "presigned URL," "S3 upload," "file validation," "upload to cloud storage," or "accept user files." Handles upload endpoints, file validation (type, size, magic bytes), cloud storage integration, and upload status tracking. For image/video processing after upload, see media-transcoder.