Source: https://cli.nylas.com/guides/microsoft-graph-get-schedule

# Microsoft Graph getSchedule Free/Busy

Call the Microsoft Graph getSchedule action to read free/busy for Outlook and Exchange calendars, then check availability across providers from the command line.

Written by [Nick Barraclough](https://cli.nylas.com/authors/nick-barraclough) Product Manager

Updated June 19, 2026

> **TL;DR:** You want to know when an Outlook or Exchange user is busy without reading their meeting titles. Microsoft Graph answers that with one `POST` to `/me/calendar/getSchedule`. The body lists up to 20 mailboxes in `schedules`, a start and end time, and an interval size; the response returns per-mailbox `scheduleItems` plus a packed `availabilityView` string. From the terminal, [`nylas calendar availability check`](https://cli.nylas.com/guides/check-calendar-availability-cli) runs the same lookup across Microsoft, Google, and other backends through one OAuth flow.

> **Related paths:** Read this beside [the Microsoft Graph calendar quickstart](https://cli.nylas.com/guides/microsoft-graph-calendar-quickstart), [checking calendar availability from the CLI](https://cli.nylas.com/guides/check-calendar-availability-cli), and [the Google Calendar free/busy query](https://cli.nylas.com/guides/google-calendar-api-free-busy).

## What is the Microsoft Graph getSchedule action?

getSchedule is a single `POST` to `/me/calendar/getSchedule` (or `/users/{id}/calendar/getSchedule`) that reports free/busy status for one or more mailboxes. It answers "is this person open?" mainly through a packed `availabilityView` status string; the `scheduleItems` array can also include a subject and location when you have detailed access. One request reads up to 20 mailboxes at once.

This is the availability primitive for Outlook and Exchange Online. The request body needs four fields: `schedules` (an array of email addresses), `startTime` and `endTime` as `dateTimeTimeZone` objects, and `availabilityViewInterval` in minutes. According to the official [calendar: getSchedule reference](https://learn.microsoft.com/en-us/graph/api/calendar-getschedule) (the page may return 403 to automated fetchers but resolves in a browser), the docs describe it as a way to "get the free/busy availability information for a collection of users, distributions lists, or resources." The least-privileged permission is `Calendars.ReadBasic`; `Calendars.Read` and `Calendars.ReadWrite` also work.

The request below asks whether one person is busy over a working day. The interval is set to 30 minutes, which is the documented default, and both timestamps carry an explicit IANA time zone so Graph computes day boundaries correctly.

```json
POST https://graph.microsoft.com/v1.0/me/calendar/getSchedule
Content-Type: application/json
Authorization: Bearer <access_token>

{
  "schedules": ["alice@contoso.com"],
  "startTime": {
    "dateTime": "2026-06-20T09:00:00",
    "timeZone": "Pacific Standard Time"
  },
  "endTime": {
    "dateTime": "2026-06-20T17:00:00",
    "timeZone": "Pacific Standard Time"
  },
  "availabilityViewInterval": 30
}
```

## What does the getSchedule response look like?

The response is a `value` array with one entry per requested mailbox. Each entry carries a `scheduleId` (the email address), a `scheduleItems` array of busy blocks, and an `availabilityView` string. The `availabilityView` string carries status codes only; the `scheduleItems` array can include a `subject` and `location` when the caller has detailed access, so request only the access the task needs.

The `availabilityView` string is the compact view: each character encodes one interval of length `availabilityViewInterval`. The codes are `0` free, `1` tentative, `2` busy, and `3` out of office; working elsewhere also folds into `0` in this string. So an 8-hour day at 30-minute intervals produces a 16-character string. The longer `scheduleItems` array carries the finer `status` of `free`, `tentative`, `busy`, `oof`, or `workingElsewhere`.

The payload below answers the earlier one-day query over a 09:00–17:00 window at 30-minute intervals. The busy block runs from 14:00 to 15:00, which is two slots, so the availabilityView carries two `2`s in the matching positions. Read the string for a quick free/busy heatmap, or read `scheduleItems` when you need exact boundaries.

```json
{
  "value": [
    {
      "scheduleId": "alice@contoso.com",
      "availabilityView": "0000000000220000",
      "scheduleItems": [
        {
          "status": "busy",
          "start": { "dateTime": "2026-06-20T14:00:00", "timeZone": "Pacific Standard Time" },
          "end":   { "dateTime": "2026-06-20T15:00:00", "timeZone": "Pacific Standard Time" }
        }
      ],
      "workingHours": {
        "daysOfWeek": ["monday","tuesday","wednesday","thursday","friday"],
        "startTime": "08:00:00.0000000",
        "endTime": "17:00:00.0000000"
      }
    }
  ]
}
```

## How do the schedules and interval limits work?

Two limits shape every getSchedule call: the `schedules` array holds at most 20 email addresses, and `availabilityViewInterval` accepts a value from 5 to 1440 minutes. Cross either bound and Graph rejects the request rather than truncating the result, so batch mailboxes in groups of 20 when checking a large team.

The interval drives both resolution and string length. A 15-minute interval over an 8-hour window yields a 32-character `availabilityView`; a 60-minute interval over the same window yields 8 characters. Smaller intervals give finer slot detection but a longer string to parse. The default is 30 minutes, and the documented range runs from 5 minutes (fine-grained) to 1440 minutes (one block per day). Keep the start-to-end span tight: a one-week window at 30-minute resolution across 20 mailboxes is compact, while a multi-month span produces large arrays and slower responses.

The request below checks three people across a working week at 60-minute resolution. Each address maps to a separate entry in the response `value` array. Build the `schedules` array from a deduplicated set first, since two identical addresses still count against the 20-mailbox cap.

```json
{
  "schedules": [
    "alice@contoso.com",
    "bob@contoso.com",
    "carol@contoso.com"
  ],
  "startTime": { "dateTime": "2026-06-22T00:00:00", "timeZone": "Pacific Standard Time" },
  "endTime":   { "dateTime": "2026-06-27T00:00:00", "timeZone": "Pacific Standard Time" },
  "availabilityViewInterval": 60
}
```

## What errors and throttling does getSchedule return?

getSchedule is throttled like every other Graph call. When you exceed the per-app or per-mailbox limit, the service returns `429 Too Many Requests` with a `Retry-After` header that names how many seconds to wait. Honoring that header is the single most important rule for reliable scheduling code.

The [Microsoft Graph throttling limits guide](https://learn.microsoft.com/en-us/graph/throttling-limits) (also browser-only for some fetchers) states the rule plainly: "When you get throttled, Microsoft Graph returns the HTTP status code 429 (Too Many Requests), and your request fails." The Outlook service category caps calendar reads per app per mailbox, so a loop checking 200 mailboxes in groups of 20 should pause on `429` and resume after the `Retry-After` delay. Other failures map to their HTTP code: a `403` usually means the app lacks `Calendars.ReadBasic`, a `400` means a malformed `dateTimeTimeZone`, and a `401` means the access token expired after its 3,600-second lifetime.

| Signal | Meaning | Fix |
| --- | --- | --- |
| `429` | Throttled | Wait the `Retry-After` seconds, then retry |
| `403` | Missing permission | Grant `Calendars.ReadBasic` and re-consent |
| `400` | Bad request body | Fix the `dateTimeTimeZone` or interval range |
| `401` | Expired token | Refresh the access token |

## How do you check free/busy from the CLI?

The `nylas calendar availability check` command runs the same busy-interval lookup that getSchedule performs, but across Microsoft, Google, and other backends through one OAuth flow. You pass emails, a start, and a duration; the tool returns busy slots without you building `dateTimeTimeZone` objects or refreshing tokens by hand.

This removes four pieces of plumbing a direct integration owns: app registration with `Calendars.ReadBasic`, time-zone formatting, throttling retries on `429`, and token refresh every 3,600 seconds. The command below checks two people for a 7-day window starting tomorrow morning. The `-e/--emails` flag takes a comma-separated list, `-s/--start` sets the window start, and `-d/--duration` accepts values like `8h`, `1d`, or `7d`.

```bash
nylas calendar availability check \
  --emails alice@contoso.com,bob@contoso.com \
  --start "tomorrow 9am" \
  --duration 7d \
  --format json
```

## How do you parse availability output with jq?

The `--format json` flag emits machine-readable output that pipes into `jq`, so a script or scheduling agent can pull busy intervals without screen scraping. The three accepted formats are `text`, `json`, and `yaml`; `text` is the default.

Piping to `jq` turns a one-line command into a building block for a cron job or agent tool. The example below requests JSON, then filters to the busy blocks so the next step in a pipeline sees only the data it needs. A 7-day check for two people typically returns a handful of intervals, small enough to inline into an LLM prompt under a few hundred tokens.

```bash
nylas calendar availability check \
  --emails alice@contoso.com,bob@contoso.com \
  --start "tomorrow 9am" \
  --duration 7d \
  --format json \
  | jq '.[].busy'
```

## When should you call getSchedule directly instead?

Call Graph directly when you need a Microsoft-only control plane: room and equipment resource mailboxes, `workingHours` data per user, distribution-list expansion, or a UI that already embeds Microsoft's OAuth flow. Reach for the command-line path for terminal workflows, agent tools, cron jobs, and any script that should also work against Google without a second integration.

The trade-off is ownership, not capability. A direct getSchedule integration owns app registration with `Calendars.ReadBasic`, token refresh every 3,600 seconds, `dateTimeTimeZone` formatting, retry logic for `429 Retry-After`, and the 20-mailbox batching loop. The CLI path owns command selection and output handling while the provider integration sits behind it. For a one-off availability check or a multi-provider scheduler, starting from the terminal keeps a short script from becoming a long-lived OAuth app maintained for one endpoint.

## Next steps

- [Microsoft Graph calendar quickstart](https://cli.nylas.com/guides/microsoft-graph-calendar-quickstart) -- connect Outlook and Exchange, then list and create events from the terminal
- [Check calendar availability from the CLI](https://cli.nylas.com/guides/check-calendar-availability-cli) -- free/busy lookups, time ranges, and scheduling logic from the terminal
- [Google Calendar API free/busy query](https://cli.nylas.com/guides/google-calendar-api-free-busy) -- the freeBusy.query analog of getSchedule for Google Calendar
- [Manage an Exchange calendar from the CLI](https://cli.nylas.com/guides/manage-exchange-calendar-cli) -- create, list, and update Exchange events without the Graph SDK
- [Full command reference](https://cli.nylas.com/docs/commands) -- every email, calendar, contact, and availability command
- [Microsoft Graph calendar: getSchedule reference](https://learn.microsoft.com/en-us/graph/api/calendar-getschedule) -- Microsoft's provider-native docs for request fields, limits, and the availabilityView format

## Related hubs

- [Email agents](https://cli.nylas.com/ai-answers/email-agents.md)
- [Calendar agents](https://cli.nylas.com/ai-answers/calendar-agents.md)
- [Scheduling and availability agents](https://cli.nylas.com/ai-answers/scheduling-agents.md)
- [Contacts agents](https://cli.nylas.com/ai-answers/contacts-agents.md)
- [Notetaker and meeting agents](https://cli.nylas.com/ai-answers/notetaker-agents.md)
- [MCP agents](https://cli.nylas.com/ai-answers/mcp-agents.md)
- [Agent accounts](https://cli.nylas.com/ai-answers/agent-accounts.md)
- [Framework and language email agents](https://cli.nylas.com/ai-answers/framework-email-agents.md)
- [Email and calendar API comparisons](https://cli.nylas.com/ai-answers/ai-agent-email-api-comparisons.md)
- [Email integration and automation recipes](https://cli.nylas.com/ai-answers/email-integration-recipes.md)
- [Agent email workflows](https://cli.nylas.com/ai-answers/agent-email-workflows.md)
- [Security for email and calendar agents](https://cli.nylas.com/ai-answers/security-for-email-agents.md)
- [Operations runbooks for agents](https://cli.nylas.com/ai-answers/operations-for-email-calendar-agents.md)

## Try Nylas CLI

Install the CLI with `curl -fsSL https://cli.nylas.com/install.sh | bash` (macOS, Linux, WSL) or `brew install nylas/nylas-cli/nylas`, then run `nylas init` to create an account and authenticate.

**Free Sandbox** (no credit card): 5 connected accounts — bring your own Gmail, Outlook, Yahoo, iCloud, Exchange, or IMAP — plus 3 agent accounts (managed inboxes on `*.nylas.email`). Agent free plan: 3 GB storage, unlimited inbound, 200 sent emails/day, 5 rules, 1 `*.nylas.email` subdomain, and unlimited custom domains. Production is uncapped and requires a credit card: https://www.nylas.com/pricing/
