Calendar
Calendars and events, recurring series, invitations, online meetings and finding a free time.
Calendars and sync
Google and Microsoft 365 accounts sync their calendars when the account's calendarEnabled setting is on; IMAP accounts sync over CalDAV once their IMAP settings name a CalDAV server. The index covers the sync window (by default 90 days back to 365 days ahead). Without calendar sync, these routes answer 409 calendar_disabled; a plan without the calendar module answers 403 plan_module_unavailable.
Each calendar says whether the account can write to it (canEdit, from access), its color, time zone and, for a shared calendar, its owner.
List events
Events are listed for a time range, [start, end), by start time: singles, occurrences of recurring series and exceptions overlapping it, never the series definitions themselves. The range may span up to 400 days; pages hold up to 500 events.
| kind | Meaning |
|---|---|
single | A one-off event. |
series | A recurring event's definition: the times of its first occurrence and its recurrence rule. Read it with GET .../calendar-events/{seriesId}. |
occurrence | An unchanged instance of a series; seriesId names the series and originalStart its slot. |
exception | An instance changed on its own. |
start and end carry the instant (dateTime), the zone it was scheduled in, and for all-day events the date (an end date is exclusive). GET .../calendar-events/{eventId} adds the event's body, fetched from the provider.
Create, change and delete events
Times are given as wall-clock time in a zone ({ dateTime, timeZone }, no offset) or as a whole day ({ date }); both ends must be the same kind. A recurrence of RFC 5545 lines (without DTSTART) makes a series, evaluated in the start's zone.
- For a video link, give one of
onlineMeeting(the provider's Teams or Meet),onlineMeetingUrl(a link you already have) oronlineMeetingService(workwae creates a Zoom or Webex meeting); more than one answersmultiple_meeting_options. notify(default true) decides whether attendees hear about the change.PATCH .../calendar-events/{eventId}changes the fields given;attendeesreplaces the whole list. Addressing an occurrence changes that instance only (it becomes an exception); addressing the series changes them all.DELETE .../calendar-events/{eventId}takes its options in the query (notify,comment) and cancels the meeting for its attendees when the account organizes it.
await workwae.calendarEvents.update({
accountId,
eventId: event.id,
subject: "Weekly sync (moved)",
start: { dateTime: "2026-11-02T11:00:00", timeZone: "Europe/Berlin" },
end: { dateTime: "2026-11-02T11:30:00", timeZone: "Europe/Berlin" },
});
// DELETE takes its path and query apart.
await workwae.calendarEvents.delete({
params: { accountId, eventId: event.id },
query: { notify: true, comment: "Cancelled for the holidays" },
});Answer invitations
The account's own answer is the event's selfResponse. An event the account organizes has no invitation to answer (organizer_cannot_respond).
Find a time
POST .../availability/find asks the provider for the busy times of up to 20 attendees over a range of up to 62 days and suggests common free slots, optionally inside working hours.
Each attendee comes back with its busy blocks, or an error when the provider could not tell (an unknown address, a calendar not shared); such an attendee does not constrain the suggestions. The account's own calendar counts unless includeAccount is false.
Calendar events in webhooks
Calendar changes, whoever made them, arrive as events: calendar.created, calendar.updated, calendar.deleted, calendar_event.created, calendar_event.updated (naming the changed fields) and calendar_event.deleted. calendar.initialized marks the end of the first calendar sync.