Browse the docs

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.

curl "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendars" \
  -H "Authorization: Bearer $WORKWAE_API_KEY"

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.

curl "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendar-events?start=2026-11-01T00:00:00Z&end=2026-12-01T00:00:00Z&calendarId=$CALENDAR_ID" \
  -H "Authorization: Bearer $WORKWAE_API_KEY"
kindMeaning
singleA one-off event.
seriesA recurring event's definition: the times of its first occurrence and its recurrence rule. Read it with GET .../calendar-events/{seriesId}.
occurrenceAn unchanged instance of a series; seriesId names the series and originalStart its slot.
exceptionAn 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.

curl -X POST "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendar-events" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "calendarId": "'"$CALENDAR_ID"'",
  "subject": "Weekly sync",
  "start": { "dateTime": "2026-11-02T10:00:00", "timeZone": "Europe/Berlin" },
  "end": { "dateTime": "2026-11-02T10:30:00", "timeZone": "Europe/Berlin" },
  "attendees": [{ "name": "Ada", "address": "[email protected]" }],
  "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=10"],
  "onlineMeeting": true
}'
  • For a video link, give one of onlineMeeting (the provider's Teams or Meet), onlineMeetingUrl (a link you already have) or onlineMeetingService (workwae creates a Zoom or Webex meeting); more than one answers multiple_meeting_options.
  • notify (default true) decides whether attendees hear about the change.
  • PATCH .../calendar-events/{eventId} changes the fields given; attendees replaces 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.
SDK
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

curl -X POST "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendar-events/$EVENT_ID/respond" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "response": "accepted", "comment": "See you there" }'

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.

curl -X POST "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/availability/find" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "attendees": ["[email protected]", "[email protected]"],
  "start": "2026-11-02T00:00:00Z",
  "end": "2026-11-07T00:00:00Z",
  "durationMinutes": 45,
  "timeZone": "Europe/Berlin",
  "workingHours": { "start": "09:00", "end": "17:00" }
}'

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.