Compare providers
See what each calendar can do before you build on it, straight from the capabilities every provider declares.
Calendar APIs agree on less than you'd hope. Google can split a recurring series in two, Outlook can't, and Cal.com names every booking after its event type no matter what title you send. Every provider declares what it supports in calendar.capabilities, and most of this table comes straight from that data. A dash means no, and "Some" means it depends on a condition, explained below the table.
| Feature | Outlook | CalDAV | Cal.com | Calendly | Zoom | ICS | Mock | |
|---|---|---|---|---|---|---|---|---|
| Write events | Yes | Yes | Yes | Some | Some | Yes | - | Yes |
| Keeps title | Yes | Yes | Yes | - | - | Yes | - | Yes |
| All-day | Yes | Yes | Yes | - | - | - | Some | Yes |
| Recurring | Yes | Yes | Yes | - | - | Some | Some | Yes |
| This and following | Yes | - | - | - | - | - | - | Yes |
| Attendees | Yes | Yes | Yes | Yes | Yes | Yes | Some | Yes |
| Video links | Yes | Some | - | - | - | Yes | - | Yes |
| Reminders | Yes | Some | - | - | - | - | - | Yes |
| Colors | Yes | - | - | - | - | - | - | Yes |
ifMatch | Yes | Yes | Yes | - | - | - | - | Yes |
| Search | Yes | - | - | - | - | - | - | Yes |
| Free/busy API | Yes | - | - | - | - | - | - | - |
| Sync | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
Conditions
Where the table says Some:
- Outlook adds Teams links only on work or school accounts.
- Outlook keeps one popup reminder per event.
- Cal.com books, moves (as a new booking with a new id) and cancels.
- Calendly books and cancels;
updateEventthrows. - Zoom repeats daily, weekly on set weekdays, or monthly on a day or an nth weekday, for up to 60 meetings or until a date. One meeting of a series can only be moved, resized or deleted.
- ICS feeds are read only: all-day, recurring and attendee data is read but can't be written.
"Keeps title" matters for the scheduling tools. Cal.com and Calendly name bookings after their event type, so a title you pass is ignored with a warning rather than saved.
Free/busy works on every provider. Where there's no endpoint, calsdk computes it from events and returns a warning, so findFreeTime behaves the same everywhere. Search is different: providers without it throw UnsupportedFunctionalityError, and you filter the listed events yourself.
Webhooks are part of the provider contract, but no provider implements them yet. Until they do, run sync on a schedule.
Check at runtime
const { events } = calendar.capabilities;
if (events.recurrenceScopeThisAndFollowing === true) {
await updateEvent({ calendar, eventId, scope: 'thisAndFollowing', patch });
}Values are true, false, 'unknown' or { condition: string }. Treat anything but true as "check first": a condition means it works for some accounts or some inputs, not all of them. See Capabilities.