Skip to content

How to list Microsoft calendar events

If you’re building an app that reads calendar data from Microsoft 365 or Outlook accounts, you can either work directly with the Microsoft Graph API or use Nylas as a unified layer that handles the provider differences for you.

With Nylas, the API call to list events is identical whether the account is Microsoft, Google, or iCloud. The differences show up in timezone handling, Teams conferencing data, recurring event behavior, and admin consent requirements. This guide covers all of that.

Why use Nylas instead of Microsoft Graph directly?

Section titled “Why use Nylas instead of Microsoft Graph directly?”

The Microsoft Graph Calendar API is powerful, but integrating it means dealing with Azure AD app registration, OAuth scope configuration, MSAL token refresh logic, admin consent flows for enterprise tenants, and Windows timezone ID mapping (Graph returns identifiers like “Eastern Standard Time” instead of IANA timezones). You also need to handle Microsoft’s specific data formats for recurring events, conferencing links, and all-day event boundaries.

Nylas normalizes all of that behind a single REST API. Your code stays the same whether you’re reading from Outlook, Google Calendar, or iCloud. No Azure AD setup, no MSAL token lifecycle, no mapping Windows timezone IDs to IANA in your application code. If you need to support multiple calendar providers or want to skip the Graph onboarding, Nylas is the faster path.

That said, if you only need Microsoft calendars and already have Graph experience, direct integration works fine.

You’ll need:

  • A Nylas application with a valid API key
  • A grant for a Microsoft 365 or Outlook account
  • The Calendars.Read scope enabled in your Azure AD app registration

New to Nylas? Start with the quickstart guide to set up your app and connect a test account before continuing here.

Microsoft organizations often require admin approval before third-party apps can access calendar data. If your users see a “Need admin approval” screen during auth, their organization restricts user consent.

You have two options:

  • Ask the tenant admin to grant consent for your app via the Azure portal
  • Configure your Azure app to request only permissions that don’t need admin consent

Nylas has a detailed walkthrough: Configuring Microsoft admin approval. If you’re targeting enterprise customers, you’ll almost certainly need to deal with this.

You also need to be a verified publisher. Microsoft requires publisher verification since November 2020, and without it users see an error during auth.

Make a List Events request with the grant ID and a calendar_id. Nylas returns the most recent events by default. You can use primary as the calendar_id to target the account’s default calendar. These examples limit results to 5:

The calendar_id parameter is required for all Events endpoints. For Microsoft accounts, calendar IDs are long base64-encoded strings, but you can use primary as a shortcut to target the default calendar. The response format is the same regardless of provider, so your parsing logic works across Microsoft, Google, and iCloud without changes.

You can narrow results with query parameters. Here’s what works with Microsoft accounts:

ParameterWhat it doesExample
calendar_idRequired. Filter by calendar?calendar_id=primary
titleMatch on event title (case insensitive)?title=standup
descriptionMatch on description (case insensitive)?description=quarterly
locationMatch on location (case insensitive)?location=Room%20A
startEvents starting at or after a Unix timestamp?start=1706000000
endEvents ending at or before a Unix timestamp?end=1706100000
attendeesFilter by attendee email (comma-delimited)[email protected]
busyFilter by busy status?busy=true
metadata_pairFilter by metadata key-value pair?metadata_pair=project_id:abc123

Microsoft also supports several additional filter parameters beyond the standard set:

  • show_cancelled - include cancelled events in results (defaults to false)
  • tentative_as_busy - treat tentative events as busy when checking availability
  • updated_after / updated_before - filter by last-modified timestamp, useful for incremental sync
  • ical_uid - find a specific event by its iCalendar UID
  • master_event_id - list all instances and overrides for a specific recurring series

Combining filters works the way you’d expect. This example pulls events in a specific time range:

A few provider-specific details that matter when you’re building against Microsoft calendar accounts.

Microsoft Graph stores timezone information using Windows timezone identifiers like “Eastern Standard Time” or “Pacific Standard Time” rather than IANA identifiers like “America/New_York” or “America/Los_Angeles”. Nylas normalizes these automatically, so event times in API responses always use IANA timezone identifiers. You don’t need to maintain a Windows-to-IANA mapping table in your application.

When Nylas returns an all-day event (a datespan object) from Microsoft, the end_date is set to the day after the event actually ends. This matches how Microsoft Graph represents all-day events internally. A single-day event on December 1st comes back with start_date: "2024-12-01" and end_date: "2024-12-02".

If you’re displaying events in a calendar UI, subtract one day from the end_date to show the correct range. This behavior is the same for Google, so your display logic doesn’t need to be provider-specific.

Events created with Microsoft Teams conferencing include a conferencing object in the response with provider set to "Microsoft Teams". The details array contains the join URL and any dial-in information. If you’re building a meeting list or join button, check for this field on every event - it’s only present when the organizer added Teams to the invite.

Microsoft treats tentative events as busy by default when calculating availability. The tentative_as_busy query parameter controls this. If your app needs to distinguish between confirmed and tentative events, check the status field on each event object.

Microsoft calendar IDs are long base64-encoded strings like AAMkAGI2TG93AAA=. These are stable and safe to store, but they take up more space than Google’s shorter numeric IDs. For the account’s default calendar, use primary as a shortcut instead of fetching the full ID. If you need to list events from a non-default calendar, call List Calendars first to get the available calendar IDs.

Microsoft handles recurring events differently from Google in a few important ways:

  • Overrides are removed on recurrence change. If you modify a recurring series (for example, changing from weekly to daily), Microsoft removes all existing overrides. Google keeps them if they still fit the pattern.
  • No multi-day monthly BYDAY. You can’t create a monthly recurring event on multiple days of the week (like the first and third Thursday). Microsoft’s recurrence model doesn’t support different indices within a single rule.
  • EXDATE recovery isn’t possible. Once you remove an occurrence from a recurring series, you can’t undo it through Nylas. You’d need to create a separate standalone event to fill the gap.
  • Rescheduling constraints. Microsoft Exchange won’t let you move a recurring instance to the same day as, or the day before, the previous instance. Overlapping instances within a series aren’t allowed.

For the full breakdown of Google vs. Microsoft recurring event differences, see Recurring events.

When the organizer deletes a recurring event instance, Microsoft and Google handle it differently. Google marks deleted occurrences as “cancelled” and keeps them retrievable with show_cancelled=true. Microsoft removes them entirely from the system - you can’t get them back through the API.

For non-recurring events, deletion behavior is more straightforward: the event disappears from list results. Use show_cancelled=true if you need to see events that participants declined or that the organizer cancelled but hasn’t fully removed yet.

Microsoft throttles API requests at the per-mailbox level. If your app triggers a 429 response, Nylas handles the retry automatically with appropriate backoff, so you don’t need to implement retry logic yourself.

If you’re polling a calendar frequently, you’ll burn through rate limits fast. Webhooks solve this by notifying you of changes in real time without any polling requests.

Calendar events typically appear within seconds of being created or updated. If an event you know exists isn’t showing up in list results yet, wait a moment and retry. This is a Microsoft-side sync delay, not a Nylas one.

For apps that need real-time awareness of calendar changes, use webhooks instead of polling. Nylas pushes a notification to your server as soon as the event syncs.

The Events API returns paginated responses. When there are more results, the response includes a next_cursor value. Pass it back as page_token to get the next page:

Keep paginating until the response comes back without a next_cursor.