Cal.com API Integration: Add Smart Scheduling to Your Next.js App
Cal.com's API lets you embed scheduling directly into your product. Learn to create booking flows, sync availability, and handle webhooks for a seamless scheduling experience.
Why Embed Scheduling Instead of Linking Out
Linking to your Cal.com page works, but embedding the scheduling flow keeps users in your product and allows for custom pre-booking data collection. The Cal.com API gives you full control over the booking flow while Cal.com handles the complexity of calendar sync, timezone handling, and email notifications.
Setting Up the Cal.com API Client
// lib/cal.ts
const CAL_API_BASE = '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_BASE}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${CAL_API_KEY}`,
'Content-Type': 'application/json',
'cal-api-version': '2024-08-13',
...options.headers,
},
});
if (!response.ok) {
throw new Error(`Cal.com API error: ${response.status}`);
}
return response.json();
}
export async function getAvailability(eventTypeId: number, dateFrom: string, dateTo: string) {
return calFetch(`/slots/available?eventTypeId=${eventTypeId}&startTime=${dateFrom}&endTime=${dateTo}`);
}
export async function createBooking(data: {
eventTypeId: number;
start: string;
attendee: { name: string; email: string; timeZone: string };
metadata?: Record<string, string>;
}) {
return calFetch('/bookings', {
method: 'POST',
body: JSON.stringify(data),
});
}
Building the Availability Picker
'use client';
import { useState, useEffect } from 'react';
interface TimeSlot {
time: string;
available: boolean;
}
export function AvailabilityPicker({
eventTypeId,
onSelect,
}: {
eventTypeId: number;
onSelect: (slot: string) => void;
}) {
const [slots, setSlots] = useState<TimeSlot[]>([]);
const [selectedDate, setSelectedDate] = useState(new Date());
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
const dateStr = selectedDate.toISOString().split('T')[0];
const nextDay = new Date(selectedDate);
nextDay.setDate(nextDay.getDate() + 1);
fetch(`/api/availability?eventTypeId=${eventTypeId}&from=${dateStr}&to=${nextDay.toISOString().split('T')[0]}`)
.then(r => r.json())
.then(data => {
setSlots(data.slots?.[dateStr] ?? []);
setLoading(false);
});
}, [selectedDate, eventTypeId]);
return (
<div>
<input
type="date"
value={selectedDate.toISOString().split('T')[0]}
onChange={e => setSelectedDate(new Date(e.target.value))}
min={new Date().toISOString().split('T')[0]}
className="mb-4 p-2 border rounded"
/>
{loading ? (
<p>Loading availability...</p>
) : (
<div className="grid grid-cols-3 gap-2">
{slots.map(slot => (
<button
key={slot.time}
onClick={() => onSelect(slot.time)}
disabled={!slot.available}
className="p-2 text-sm border rounded hover:bg-blue-50 disabled:opacity-40"
>
{new Date(slot.time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</button>
))}
</div>
)}
</div>
);
}
Handling Webhooks
// app/api/webhooks/cal/route.ts
export async function POST(request: Request) {
const body = await request.json();
const signature = request.headers.get('X-Cal-Signature-256');
// Verify webhook signature
const expectedSig = await createHmacSignature(
process.env.CAL_WEBHOOK_SECRET!,
JSON.stringify(body)
);
if (signature !== expectedSig) {
return new Response('Unauthorized', { status: 401 });
}
const { triggerEvent, payload } = body;
switch (triggerEvent) {
case 'BOOKING_CREATED':
await handleBookingCreated(payload);
break;
case 'BOOKING_CANCELLED':
await handleBookingCancelled(payload);
break;
case 'BOOKING_RESCHEDULED':
await handleBookingRescheduled(payload);
break;
}
return new Response('OK');
}
Passing Custom Data to Bookings
Use the metadata field to pass context from your app to Cal.com bookings. This appears in Cal.com and webhook payloads, letting you link bookings back to your users, orders, or any app-specific context without any extra state management.
Admin
Cal.com
Open source scheduling — tự host booking system, thay thế Calendly. Free & privacy-first.
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!