# Microsoft Graph — Calendar  · replaces agent365 `calendar`

Paging: add `max_items=N` to any GET (≤ 1000). Coverage + limits: see the SKILL.md guide.

| Task | Call |
|------|------|
| Upcoming events | `GET /me/events?$select=subject,start,end,location,organizer&$top=20&$orderby=start/dateTime` |
| Events in a date window | `GET /me/calendarView?startDateTime=2026-05-30T00:00:00Z&endDateTime=2026-05-31T00:00:00Z` |
| One event | `GET /me/events/{id}` |
| My calendars | `GET /me/calendars` |
| Create | `POST /me/events` |
| Update | `PATCH /me/events/{id}` |
| Delete | `DELETE /me/events/{id}` |
| Accept / decline | `POST /me/events/{id}/accept` · `/decline` · `/tentativelyAccept` |
| Free/busy lookup | `POST /me/calendar/getSchedule` |
| Schedule as a Teams meeting | `POST /me/events` + `"isOnlineMeeting": true, "onlineMeetingProvider": "teamsForBusiness"` → response `onlineMeeting.joinUrl` is the join link |
| Suggest meeting times | `POST /me/findMeetingTimes` (attendees + time window → ranked slots) |

Returned times are **UTC** (no `Prefer: outlook.timezone` header) — convert
client-side. `calendarView` is best for "today / this week" windows. Create body:

```json
{
  "subject": "Sync",
  "start": { "dateTime": "2026-06-01T15:00:00", "timeZone": "UTC" },
  "end":   { "dateTime": "2026-06-01T15:30:00", "timeZone": "UTC" },
  "attendees": [{ "emailAddress": { "address": "lee@contoso.com" }, "type": "required" }]
}
```

**Find a free slot, then book**: `POST /me/calendar/getSchedule` →
`{"schedules":["me@…","lee@…"],"startTime":{"dateTime":"…Z","timeZone":"UTC"},"endTime":{…},"availabilityViewInterval":30}`.
Read each returned `availabilityView` string (`0`=free) to pick a common slot,
then `POST /me/events`. Prefer this over reading each attendee's calendar and
diffing — `getSchedule` is the purpose-built call.

Incremental sync (changes only):
`GET /me/calendarView/delta?startDateTime={…}Z&endDateTime={…}Z` → save the
`@odata.deltaLink`, pass it back next run (delta pattern: SKILL.md).
