Cal.com
Read, book, move and cancel Cal.com bookings as calendar events.
Cal.com is a scheduling tool, not a calendar, so here the events are bookings. Use this provider to read a user's bookings, book a slot on one of their event types, reschedule it or cancel it, with the same functions you'd use for Google. Expect the booking rules to win: the event type decides the title, the length and most other fields.
npm install @cal-sdk/core @cal-sdk/calcomimport { createEvent, listEvents } from '@cal-sdk/core';
import { calcom } from '@cal-sdk/calcom';
const calendar = calcom({ accessToken: process.env.CALCOM_API_KEY! });
const { events } = await listEvents({
calendar,
timeMin: '2026-11-01T00:00:00Z',
timeMax: '2026-12-01T00:00:00Z',
});
// Book event type 123456 for a guest.
await createEvent({
calendar,
calendarId: '123456',
event: {
start: { dateTime: '2026-11-03T10:00:00', timeZone: 'Europe/Berlin' },
end: { dateTime: '2026-11-03T10:30:00', timeZone: 'Europe/Berlin' },
attendees: [{ email: 'guest@example.com', name: 'Guest' }],
},
});Credentials
accessToken is a Cal.com API key (cal_live_...) or an OAuth access token.
Settings
- accessTokenstring | (() => string | PromiseLike<string>)
- API key or OAuth access token, or a function that returns one.
- baseURL?string= 'https://api.cal.com/v2'
- Use 'https://api.cal.eu/v2' for Cal.com EU.
- headers?Record<string, string | undefined>
- Headers sent with every request.
- fetch?typeof fetch
- A custom fetch, for example to log or record requests.
Calendars
'primary' holds every booking of the user. Each event type is also a calendar, whose id is the event type id (such as '123456'), holding that event type's bookings.
Not supported
All-day and recurring events, ifMatch and search throw UnsupportedFunctionalityError. Free/busy is computed from bookings. See the comparison for every provider.
How it behaves
Booking
Creating an event books an event type: the calendar's, or providerOptions.calcom.eventTypeId on 'primary'. The first attendee books the slot and the others are added as guests. Without attendees, the user books it for themselves. The description becomes the booking's notes. Cal.com names bookings after their event type, so title, and fields the event type sets (location, createConference, reminders, color, visibility), are ignored with a warning.
A booking lasts as long as its event type, or one of the lengths the event type offers. Other lengths throw InvalidArgumentError. As with any other calendar, calsdk writes the times you give it, so by default a booking may overlap others and fall outside the event type's availability. To have Cal.com enforce its rules, set allowConflicts or allowBookingOutOfBounds to false.
Moving and cancelling
An event id is the booking's uid, and that id doesn't survive a move. Updating can only move a booking, and Cal.com moves a booking by replacing it, so the returned event has a new id and the old id reads as deleted. If you store booking ids, update them after every move. Changes to other fields are ignored with a warning, and the booking keeps the time zone it was made in.
Deleting cancels the booking. Cancelled and rejected bookings read as deleted events, and bookings waiting for confirmation are tentative. Cal.com always emails attendees, so sendUpdates values other than 'all' return a warning.
Listing and sync
Listing finds bookings by their start, because Cal.com can't filter them by their end. To catch bookings that started before timeMin and are still running, calsdk looks back as far as the longest length your event types allow (at least a day). A booking that started before that lookback is missed: one of a team event type you don't own, or one longer than its event type now allows.
Sync reads bookings by the time they last changed. A booking saved in a slow transaction can show up after later ones, so each sync reads the minute before the newest change it saw again, and that booking isn't missed. A first sync returns active bookings only.
Provider options
await createEvent({
calendar,
event,
providerOptions: {
calcom: { eventTypeId: 123456, metadata: { orderId: '42' }, allowConflicts: false },
},
});
await updateEvent({
calendar,
eventId,
patch: { start, end },
providerOptions: { calcom: { reschedulingReason: 'Travel' } },
});
await deleteEvent({
calendar,
eventId,
providerOptions: { calcom: { cancellationReason: 'No longer needed' } },
});createEvent
- eventTypeId?number
- Required on
'primary'. - metadata?Record<string, string>
- Stored on the booking.
- allowConflicts?boolean= true
- Book over other bookings and busy time.
- allowBookingOutOfBounds?boolean= true
- Book outside the event type's availability, notice and booking window.
updateEvent
- reschedulingReason?string
- Shown in the reschedule email.
- allowConflicts?boolean= true
- As on
createEvent. - allowBookingOutOfBounds?boolean= true
- As on
createEvent.
deleteEvent
- cancellationReason?string= 'Cancelled'
- Shown in the cancellation email.
Metadata
Events carry providerMetadata.calcom:
| Field | Type and notes |
|---|---|
bookingId | number, Cal.com's numeric id. The event id is the uid |
eventTypeId | number |
status | string: 'accepted', 'pending' or 'awaiting_host' |
rescheduledFromUid | string, the booking this one replaced |
metadata | Record<string, string> |