Calendar

List and manage calendars and events, answer invitations and find a time when attendees are free.

Read and write an account's calendars and events, answer invitations, and ask when people are free. To let other people book time with your users, see Scheduling links.

Calendars and sync

Calendar routes read Workwae's index of the account's calendars.

  • Google and Microsoft 365 accounts sync calendars while 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.
StatusReasonWhen
409calendar_disabledThe account does not sync calendars.
403plan_module_unavailableThe plan has no calendar module.
curl "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendars" \
  -H "Authorization: Bearer $WORKWAE_API_KEY"

Each calendar has canEdit (whether the account can write to it, from access), its color, time zone and, for a shared calendar, its owner.

Manage calendars

POST, PATCH and DELETE /v1/accounts/{accountId}/calendars create, change and delete calendars. Providers store different fields:

ProviderStoresRefused as provider_unsupported
Microsoft 365Name, color (#rrggbb)Description, time zone (the mailbox zone applies to every calendar)
GoogleName, description, time zone, color—
CalDAVName, description, color (Apple calendar-color)Time zone
  • The primary calendar can be renamed where the provider allows it, but not deleted (calendar_primary).
  • A calendar the account cannot write answers calendar_read_only.
  • Deleting a calendar removes its events from the index without announcing them one by one. A provider that still holds events deletes them with the calendar.

List events

Events are listed for a time range [start, end), ordered by start time. The list holds singles, occurrences of recurring series and exceptions that overlap the range, never series definitions. The range spans up to 400 days; pages hold up to 500 events (default 100).

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 events

Give times 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": "ada@example.com" }],
  "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=10"],
  "onlineMeeting": true
}'

For a video link, set one of these; more than one answers multiple_meeting_options:

FieldLink
onlineMeetingThe provider's own: Teams on Microsoft 365, Meet on Google.
onlineMeetingUrlA link you already have.
onlineMeetingServiceA Zoom or Webex meeting Workwae creates. See Meetings.

notify (default true) decides whether attendees hear about the change.

Change and delete events

  • 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). When the account organizes the event, it cancels the meeting for the attendees.
curl -X PATCH "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendar-events/$EVENT_ID" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "start": { "dateTime": "2026-11-02T11:00:00", "timeZone": "Europe/Berlin" },
  "end": { "dateTime": "2026-11-02T11:30:00", "timeZone": "Europe/Berlin" }
}'

curl -X DELETE "https://api.workwae.com/v1/accounts/$ACCOUNT_ID/calendar-events/$EVENT_ID?notify=true&comment=Cancelled%20for%20the%20holidays" \
  -H "Authorization: Bearer $WORKWAE_API_KEY"

Answer invitations

response is accepted, tentative or declined. The account's own answer is the event's selfResponse. An event the account organizes has no invitation to answer (organizer_cannot_respond).

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": "tentative", "comment": "May run late" }'

Find a time

POST /v1/accounts/{accountId}/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": ["ada@example.com", "alan@example.com"],
  "start": "2026-11-02T00:00:00Z",
  "end": "2026-11-07T00:00:00Z",
  "durationMinutes": 45,
  "timeZone": "Europe/Berlin",
  "workingHours": { "start": "09:00", "end": "17:00" }
}'
OptionDefaultMeaning
durationMinutes30Slot length, 5 to 1440.
stepMinutes30Distance between suggested starts.
timeZoneUTCZone the working hours are read in.
maxSuggestions10Up to 100.
includeAccounttrueCount the account's own calendar.
requireAllfalseSuggest nothing while an attendee is unresolved.
  • One provider request covers every attendee; the answer is cached briefly per account, attendees and range.
  • 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 is listed in unresolved, complete is false, and it does not constrain the suggestions.
  • With requireAll: true, an unresolved attendee withholds every suggestion (withheldReason: "unresolved_attendees").

Across several accounts

POST /v1/availability/find reads up to 20 of the tenant's accounts, each through its own calendar. With mode: "all" it suggests slots where every account is free; with mode: "any" (round-robin) slots where at least one is, naming the free accounts per slot. Extra attendees are looked up through the first account and must be free in either mode.

curl -X POST "https://api.workwae.com/v1/availability/find" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "accountIds": ["'"$ALICE_ACCOUNT_ID"'", "'"$BOB_ACCOUNT_ID"'"],
  "mode": "any",
  "start": "2026-11-02T00:00:00Z",
  "end": "2026-11-07T00:00:00Z",
  "timeZone": "Europe/Berlin"
}'

An account that cannot be read (calendar sync off, paused, needing a new sign-in, a provider failure) is listed in unresolved with its error. In mode all it does not constrain the suggestions; in mode any it is never offered as free. requireAll withholds every suggestion instead. Keys limited to specific accounts cannot use this route.

To offer these slots to outside people and book them, create a scheduling link instead.

Calendar events in webhooks

Changes made anywhere 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. Payloads are in Calendar and contacts events.

On this page