# Microsoft OAuth scopes for email and calendar

Source: https://developer.nylas.com/docs/cookbook/use-cases/build/microsoft-oauth-scopes/

Microsoft deprecated Basic Auth for Exchange Online in October 2022, so every Outlook and Microsoft 365 account now connects through OAuth 2.0 and Microsoft Graph permissions. This page lists the exact Graph scopes Nylas requests for each email, calendar, and contacts feature, so you grant the narrowest set that still works.

For the generic cross-provider scope strings (Google plus Microsoft side by side), see [OAuth scopes for email and calendar](/docs/cookbook/use-cases/build/oauth-scopes-email-calendar/). This page is the Microsoft-only deep reference.

## What Microsoft Graph scopes does Nylas request?

Nylas requests Microsoft Graph delegated permissions: short names like `Mail.Read` with no URI prefix, scoped to the signed-in user rather than the whole tenant. The 7 scopes below cover reading and sending mail, managing calendars and events, and reading contacts. Add `offline_access` to every set so the access token can refresh.

The table maps each Nylas feature to the Graph scope it needs. These names are verified against the Microsoft tab in the [granular scopes reference](/docs/dev-guide/scopes/) and the OpenAPI `x-scopes`, so you can copy them into your Azure app registration without guessing.

| Feature                          | Required Graph scope  | Wider scope that also works |
| -------------------------------- | --------------------- | --------------------------- |
| Read messages and attachments    | `Mail.Read`           | `Mail.ReadWrite`            |
| Update or delete messages         | `Mail.ReadWrite`      | `Mail.ReadWrite.Shared`     |
| Send email                        | `Mail.Send`           | None                           |
| Read and write drafts             | `Mail.ReadWrite`      | `Mail.ReadWrite.Shared`     |
| Read and write folders            | `Mail.ReadWrite`      | None                           |
| Read calendars and Free/Busy      | `Calendars.Read`      | `Calendars.ReadWrite`       |
| Create or update events           | `Calendars.ReadWrite` | None                           |
| Read contacts                     | `Contacts.Read`       | `People.Read`               |
| Read and write contacts           | `Contacts.ReadWrite`  | None                           |
| Read room and resource calendars  | `Place.Read.All`      | None                           |

Mail and calendar permissions are separate in Graph, so a calendar-only app never requests a `Mail.*` scope. Request `Mail.ReadWrite` and `Mail.Send` together when you schedule messages for future delivery, which the scopes reference calls out for [scheduled send](/docs/v3/email/scheduled-send/).

Two scopes tie to specific Nylas features rather than to a basic read or write action. `Place.Read.All` powers `GET /v3/grants/<NYLAS_GRANT_ID>/resources`, which lists bookable room and equipment resources for meeting scheduling. `People.Read` is required for the contacts `inbox` and `domain` sources, which surface people the user has emailed or who share their tenant directory. Skip both unless your app uses room booking or directory-derived contacts.

## Why does Nylas need offline_access?

Microsoft delegated access tokens expire after about 60 to 90 minutes. `offline_access` is the Graph scope that returns a long-lived refresh token alongside the access token, and without it the grant stops working after the first token expires with no way to renew. Nylas refreshes the token for you once the scope is granted.

Add `offline_access` to every scope set, even a read-only one. The hosted OAuth flow at `GET /v3/connect/auth` sends the connector's scopes to Microsoft, the user consents once, and the returned refresh token keeps the grant alive until the user or the tenant admin revokes it. Without the refresh token, a background sync job that runs every few hours fails the moment the first 60-minute access token expires, and the grant reports an authentication error until the user reconnects.

The example below shows a read-and-send Microsoft scope set on the authorization request. The four scope names are space-separated in the `scope` query parameter, exactly as Microsoft expects them.

```bash
GET https://api.us.nylas.com/v3/connect/auth?
  client_id=<NYLAS_CLIENT_ID>
  &redirect_uri=https://myapp.com/callback
  &response_type=code
  &provider=microsoft
  &scope=Mail.Read Mail.Send Calendars.ReadWrite offline_access
```

## Delegated vs application permissions in Microsoft Graph

Microsoft Graph offers two permission types, and Nylas uses delegated permissions exclusively. Delegated permissions act on behalf of the signed-in user during an interactive OAuth flow, so the grant can only touch data that user can already see. Application permissions act tenant-wide with no user present and always require admin consent before any account connects.

The distinction decides whether a single admin click or per-user consent unblocks your app. Delegated scopes like `Mail.Read` let an ordinary user consent for their own mailbox on the standard consent screen. In a locked-down Microsoft 365 tenant, an administrator can still gate even delegated scopes behind tenant-wide approval, which is one click that covers every user at once. Application permissions never offer per-user consent, so granting an application-type `Mail.Read` in Azure and expecting the hosted flow to use it leads to a common dead end.

Plan for admin consent when your target customers run managed tenants. A 2022 Microsoft change tightened default consent settings, so many enterprise tenants now block user consent entirely and route every new app through an admin. Walk admins through the one-time grant with the [Microsoft admin approval guide](/docs/provider-guides/microsoft/admin-approval/), and point users who hit the consent wall to [Fix the need admin approval error](/docs/cookbook/use-cases/build/fix-admin-approval-error/).

## When do you need the .Shared scopes?

You need the `.Shared` scope variants when your app reads or writes a mailbox or calendar that the signed-in user doesn't own, such as a shared support inbox or a delegate's calendar. The base `Mail.Read` scope only covers the user's own mailbox, so accessing a shared mailbox requires `Mail.Read.Shared` or `Mail.ReadWrite.Shared` on top of it.

Microsoft Graph defines 3 shared variants that map to the Nylas features below. `Mail.Read.Shared` and `Mail.ReadWrite.Shared` cover delegated mailbox folders, and `Calendars.ReadWrite.Shared` covers a delegate's calendar. The scopes reference lists these 3 scopes as "other scopes" for the messages, drafts, folders, and calendar endpoints, so a delegated request still works when the wider shared scope is present.

| Shared scenario                       | Graph scope                  |
| ------------------------------------- | ---------------------------- |
| Read a shared or delegated mailbox    | `Mail.Read.Shared`           |
| Write to a shared or delegated mailbox| `Mail.ReadWrite.Shared`      |
| Manage a delegate's calendar          | `Calendars.ReadWrite.Shared` |

Request a `.Shared` scope only when the use case demands it. A help-desk app that reads one team inbox needs `Mail.Read.Shared`, but a personal email client never does, and asking for it inflates the consent screen for no reason. The [shared Outlook folders guide](/docs/provider-guides/microsoft/shared-folders/) covers how Nylas exposes delegated mailbox folders once the scope is granted.

## How do I set Microsoft scopes in the Nylas SDKs?

You set Microsoft scopes on the connector or pass them to the SDK's OAuth URL builder, not on each API call. The `urlForOAuth2` method takes your client ID, the `microsoft` provider, your callback URI, and a `scope` array of Graph permission names. Generate the URL per user, then redirect them to Microsoft to consent.

The 5 examples below build the same Microsoft authorization URL with a set of 4 scopes: `Mail.Read`, `Mail.Send`, `Calendars.ReadWrite`, and `offline_access`. Swap the array for `["Calendars.ReadWrite", "offline_access"]` for a calendar-only app, or add `Contacts.Read` when you sync contacts.

```javascript [msSdkScopes-Node.js SDK]
const authUrl = nylas.auth.urlForOAuth2({
  clientId: process.env.NYLAS_CLIENT_ID,
  provider: "microsoft",
  redirectUri: "https://myapp.com/callback",
  scope: ["Mail.Read", "Mail.Send", "Calendars.ReadWrite", "offline_access"],
});
```

```python [msSdkScopes-Python SDK]
auth_url = nylas.auth.url_for_oauth2({
    "client_id": os.environ["NYLAS_CLIENT_ID"],
    "provider": "microsoft",
    "redirect_uri": "https://myapp.com/callback",
    "scope": ["Mail.Read", "Mail.Send", "Calendars.ReadWrite", "offline_access"],
})
```

```ruby [msSdkScopes-Ruby SDK]
auth_url = nylas.auth.url_for_oauth2({
  client_id: ENV["NYLAS_CLIENT_ID"],
  provider: "microsoft",
  redirect_uri: "https://myapp.com/callback",
  scope: ["Mail.Read", "Mail.Send", "Calendars.ReadWrite", "offline_access"]
})
```

```java [msSdkScopes-Java SDK]
UrlForAuthenticationConfig config = new UrlForAuthenticationConfig.Builder(
    System.getenv("NYLAS_CLIENT_ID"), "https://myapp.com/callback")
    .provider(AuthProvider.MICROSOFT)
    .scope(new String[]{"Mail.Read", "Mail.Send", "Calendars.ReadWrite", "offline_access"})
    .build();
String authUrl = nylas.auth().urlForOAuth2(config);
```

```kotlin [msSdkScopes-Kotlin SDK]
val config = UrlForAuthenticationConfig(
    clientId = System.getenv("NYLAS_CLIENT_ID"),
    redirectUri = "https://myapp.com/callback",
    provider = AuthProvider.MICROSOFT,
    scope = arrayOf("Mail.Read", "Mail.Send", "Calendars.ReadWrite", "offline_access"),
)
val authUrl = nylas.auth().urlForOAuth2(config)
```

## Choose the least-privilege Microsoft scope set

Request the smallest scope that covers your features, then widen only when a feature needs it. A smaller consent screen converts better, and a leaked token reaches less data. The table maps 5 common app types to the minimal Microsoft scopes each needs, from a single scope for a read-only inbox up to 4 scopes for a mail-plus-calendar app, with `offline_access` on every row.

| App type            | Microsoft Graph scopes                                    |
| ------------------- | --------------------------------------------------------- |
| Read-only inbox     | `Mail.Read`, `offline_access`                             |
| Full email client   | `Mail.ReadWrite`, `Mail.Send`, `offline_access`           |
| Calendar scheduler  | `Calendars.ReadWrite`, `offline_access`                   |
| Contacts sync       | `Contacts.Read`, `offline_access`                         |
| Mail plus calendar  | `Mail.ReadWrite`, `Mail.Send`, `Calendars.ReadWrite`, `offline_access` |

Each row maps to a real product shape. A read-only inbox app that classifies or indexes mail needs only `Mail.Read` plus `offline_access`, two scopes total, because it never modifies or sends. A full email client that marks messages read, moves them between folders, and sends replies needs `Mail.ReadWrite` for the modify and folder operations and `Mail.Send` for outbound mail. A calendar scheduler that creates and updates events needs `Calendars.ReadWrite` and nothing from the `Mail.*` family. A one-way contacts sync that only reads the address book needs `Contacts.Read`, and upgrades to `Contacts.ReadWrite` only if it writes contacts back.

Don't request `Mail.ReadWrite` when your app only reads mail. Unlike Google, Microsoft doesn't push you into a third-party security assessment for write scopes, but a tenant admin reviewing the consent screen sees every permission you ask for, and an over-broad request is a common reason a connection gets blocked in a locked-down Microsoft 365 tenant. Over-requesting also draws more scrutiny during a tenant's app review, and any later scope addition forces every connected user back through consent.

Run through this checklist before you finalize a Microsoft scope set:

1. List the exact Nylas endpoints your app calls, then map each to its required scope in the table above.
2. Drop any scope no endpoint needs, including `.Shared` variants unless you read another user's mailbox.
3. Use the read scope, not the write scope, wherever your app only reads. `Mail.Read` over `Mail.ReadWrite`, `Calendars.Read` over `Calendars.ReadWrite`.
4. Add `offline_access` so the grant can refresh, since every app needs it.
5. Add `Place.Read.All` only for room booking and `People.Read` only for `inbox` or `domain` contact sources.

## Things to know about Microsoft scopes

Beyond the delegated-versus-application split and the `.Shared` variants covered above, a few more details change how and when these scopes take effect. Each of the 4 items below maps to a real failure mode developers hit during Microsoft integration.

- **Adding a scope forces re-authentication.** Microsoft only grants new scopes at consent time. Add `Mail.Send` after a user already connected with `Mail.Read`, and their existing grant doesn't gain it. The user re-authenticates through the flow to pick up the wider access.
- **Conferencing autocreate needs an extra scope.** Set up automatic Teams links on events and you also request `OnlineMeetings.ReadWrite` alongside `Calendars.ReadWrite`. See [enable autocreate for conferencing](/docs/v3/calendar/add-conferencing/#enable-auto-conferencing) for the full setup.
- **Contacts sources change the scope.** The base `Contacts.Read` scope covers the user's personal address book, but the `inbox` and `domain` contact sources need `People.Read`. Request it only when you read directory or recent-correspondent contacts.
- **EWS scopes differ.** Exchange Web Services accounts don't use Graph permission names. Microsoft retired EWS in Exchange Online in 2022, so most accounts should connect with the Graph scopes above instead.

The canonical list lives in the [Microsoft Graph permissions reference](https://learn.microsoft.com/en-us/graph/permissions-reference). Cross-check any scope against it before you add it, because Microsoft renames and deprecates permissions over time.

## What's next

- [OAuth scopes for email and calendar](/docs/cookbook/use-cases/build/oauth-scopes-email-calendar/) for the Google and Microsoft scope strings side by side
- [Authenticating Microsoft accounts with Nylas](/docs/provider-guides/microsoft/authentication/) for the full Microsoft auth flow
- [Troubleshoot OAuth errors](/docs/cookbook/use-cases/build/troubleshoot-oauth-errors/) when a Microsoft connection fails or returns 403
- [Granular scopes reference](/docs/dev-guide/scopes/) for required and optional scopes per Nylas API endpoint