Background: When exceptions are made to recurring events Outlook makes a separate exception appointment, but does not return that appointment in the /events API. Instead you're forced to query the /calendarView API with date ranges to look for exceptions to the recurrence pattern of an event. This demands multiple API calls to every recurring event just to test whether or not exceptions exist. This is like a technical game of "Where's Waldo" for any recurring event that has an exception, which is bad if there is a recurring event that has no ending.
Example
This screenshot of my test calendar shows a situation where an occurrence of a recurring event has broken the recurrence pattern of the series master. In this case, my Monday morning meeting was moved to Tuesday due to a national holiday. This type of situation is common and will likely present itself hundreds if not thousands of times as we compile our database.
An API call to the events resource fails to capture this event.
The call: https://graph.microsoft.com/v1.0/me/calendars/{test calendar id}/events
The response:
{
"value": [
{
"@odata.etag": "W/\"n5FL1xdp8kOQHOa5po5P1AAAWQhJng==\"",
"id": "[omitted]",
"createdDateTime": "2022-06-15T16:56:49.2346883Z",
"lastModifiedDateTime": "2022-06-15T16:58:50.0089411Z",
"changeKey": "n5FL1xdp8kOQHOa5po5P1AAAWQhJng==",
"categories": [],
"transactionId": null,
"originalStartTimeZone": "UTC",
"originalEndTimeZone": "UTC",
"iCalUId": "[omitted]",
"reminderMinutesBeforeStart": 1080,
"isReminderOn": true,
"hasAttachments": false,
"subject": "Recurring Event",
"bodyPreview": "",
"importance": "normal",
"sensitivity": "normal",
"isAllDay": true,
"isCancelled": false,
"isOrganizer": true,
"responseRequested": true,
"seriesMasterId": null,
"showAs": "free",
"type": "seriesMaster",
"webLink":"[omitted]",
"onlineMeetingUrl": null,
"isOnlineMeeting": false,
"onlineMeetingProvider": "unknown",
"allowNewTimeProposals": true,
"isDraft": false,
"hideAttendees": false,
"onlineMeeting": null,
"responseStatus": {
"response": "none",
"time": "0001-01-01T00:00:00Z"
},
"body": {
"contentType": "html",
"content": "[omitted]"
},
"start": {
"dateTime": "2022-07-18T00:00:00.0000000",
"timeZone": "UTC"
},
"end": {
"dateTime": "2022-07-19T00:00:00.0000000",
"timeZone": "UTC"
},
"location": {
"displayName": "",
"locationType": "default",
"uniqueIdType": "unknown",
"address": {},
"coordinates": {}
},
"locations": [],
"recurrence": {
"pattern": {
"type": "weekly",
"interval": 1,
"month": 0,
"dayOfMonth": 0,
"daysOfWeek": [
"monday"
],
"firstDayOfWeek": "sunday",
"index": "first"
},
"range": {
"type": "endDate",
"startDate": "2022-07-18",
"endDate": "2023-01-02",
"recurrenceTimeZone": "Eastern Standard Time",
"numberOfOccurrences": 0
}
},
Discussion
The response above is of the only object pertaining to the recurring event “Recurring Event” in my test calendar. The response contains no information regarding the existence of exceptions, let alone the exceptions themselves. I’ve been told that this resource is functioning as intended and in order to receive exceptions I will need to use a different resource.
The resource “List instances” was recommended previously, but I believe this to be the least efficient way to handle the task at hand as this resource is limited to only a single series master event per API call. If we have 10’s of thousands of recurring events, a separate call for each of their instances would be highly inefficient.
The resource, “Calendar view”, is a more efficient option in my opinion as it is not limited by event. That’s not to say it doesn’t have limitations.
For example:
- It needs to be filtered to avoid the retrieval of redundant event data
- …/calendarView…&$filter=type eq ‘exception’
- It requires a window of time to be specified
- The window of time cannot exceed 1825 days (5 years)
While calendar view certainly captures far more exceptions per API call than list instances, it is still inefficient and fails to accomplish telling the entire story of a particular calendar.
Question Summary
Why aren’t exceptions included in the call to the /events resource?
From what I have gathered, occurrences aren’t included in the call to /events due to the possibility of a recurring event persisting indefinitely. The events call captures all events in a calendar and therefore could not retrieve occurrences to a recurring event with no end date. While that makes sense, it is impossible for exceptions to do the same.