# Microsoft calendar sync (Agent Skill)

> Two-way Outlook and Microsoft 365 calendar sync over Microsoft Graph: hosts connect their account, their events block availability, and confirmed bookings land in their calendar with Teams links.

Page: https://timerise.ai/skills/microsoft-calendar-sync

Agent Skill `microsoft-calendar-sync`: https://github.com/timerise-ai/microsoft-calendar-sync. Category: Scheduling. Runs on Next.js; backend: Firestore or Supabase; storage, auth and the booking source behind seams.

Latest release: v0.1.4 (2026-10-16).

Install: npx skills add timerise-ai/microsoft-calendar-sync. Timerise builds it on a brief; the price comes from the brief.

Most companies that sell to other companies live in Outlook. If a booking system cannot see a consultant's Outlook calendar, clients book times the consultant already gave away, and bookings never show up where the consultant actually looks.

Microsoft Graph makes the first demo easy and the first month hard. Refresh tokens change with every use, so a connection that worked yesterday stops working today. Notifications have to be answered within three seconds or Graph gives up on them. Subscriptions expire, and some vanish without a word. An all-day vacation floats between time zones. Get one of those wrong and the integration fails quietly, an hour or a week after it went live.

This skill gives your coding agent the whole sync, written by the engineers who build calendar integrations for booking systems: the Microsoft sign-in, busy time from the calendars a host chooses, change notifications with a renewal job that replaces lost subscriptions, booking events with Teams links, and health per calendar so a broken connection becomes a reconnect prompt. You own the code, run it on Next.js on Vercel, with Supabase or Firestore, under your own Microsoft Entra app.

Build two-way Microsoft 365 / Outlook calendar sync for a booking or scheduling backend over Microsoft Graph: hosts connect their Microsoft account with OAuth, events on chosen calendars block their availability (change-notification subscriptions plus a renewal cron), and confirmed bookings are written to their calendar as events, with Teams meeting links. Use when: (1) an app with bookings, appointments, classes or shifts must respect staff's Outlook busy time or put bookings into their Outlook calendar, (2) an existing Outlook or Graph calendar integration stops working an hour after connecting, never receives notifications, ignores vacations, duplicates events or loses its subscriptions, (3) the user mentions: Outlook calendar integration, Microsoft 365 calendar, Office 365 calendar sync, Microsoft Graph calendar, Entra ID app registration, Calendars.ReadWrite, calendarView, change notifications, Graph subscription, clientState, validationToken, lifecycleNotificationUrl, subscriptionRemoved, MSAL refresh token, AADSTS700082, invalid_grant, interaction_required, singleValueExtendedProperties, teamsForBusiness, showAs, isAllDay, mailboxSettings timeZone. Carries a dependency-free Graph client on fetch, a pure busy-time mapper that anchors all-day events to the mailbox zone, single-flight sync, booking events found again by an extended-property marker, in-place subscription renewal that replaces lost subscriptions, derived health per calendar, Firestore and Supabase stores, and a 43-test node:test suite run under four process time zones. TypeScript for Next.js App Router route handlers, with an Express adapter for any other Node server; storage, auth and the booking source are seams behind CalendarStore, authenticate and BookingSource. Not Google Calendar, not Exchange on-premises (EWS), not a calendar UI.

This skill was written by the engineer who builds the calendar integrations of a booking platform, and audited against the earlier implementation of its Microsoft side. The templates hold the properties a Graph calendar sync has to hold: a connection keeps working past its first access token because every rotated refresh token is stored; every subscription fits Graph's `clientState` limit and is bound to the notifications it signs; a notification is answered before any sync starts; a subscription Graph dropped is replaced on the next cron run; an all-day vacation blocks the host's local days in the mailbox zone; a booking pushed any number of times is one event. A 43-test `node:test` suite, run under four process time zones, states each one, and [`references/provenance.md`](references/provenance.md) has the record.

What it builds:
- OAuth connect with encrypted tokens
- Busy time from the calendars a host chooses kept current by change notifications and a renewal cron that replaces lost subscriptions
- All-day events anchored to the mailbox time zone
- Confirmed bookings written as events with Teams links
- Derived health per calendar
- An Express adapter beside the App Router routes

When to use:
- Hosts connect Microsoft 365 or Outlook.com and pick calendars that block their availability.
- Bookings appear in the host's Outlook calendar, optionally as Teams meetings, and vanish on cancel.
- Auditing an existing Graph integration: dead after an hour, silent subscriptions, missed vacations.
- Moving that sync between Firestore and Supabase/Postgres.

When not to use:
- **Google Calendar**: use `google-calendar-sync`. Same architecture and record shapes, different API.
- **Generating bookable slots, subtracting busy time**: use `time-slot-generator`. This skill *produces* the busy blocks it consumes.
- **Signing users in with Microsoft (SSO)** is an identity concern; this skill asks for calendar access of an already signed-in user.
- **Room and resource mailboxes, or organisation-wide access without per-user consent** need application permissions with admin consent, a different design.
- **Exchange Server on-premises** speaks EWS, not Graph.

Non-negotiables:
- Never take the user id from the OAuth callback URL. The state is a key to a server-side, single-use, ten-minute record that holds the user id, so nobody can attach their calendar to someone else's account. The connect tests consume the state, refuse it a second time and after expiry, and read the user from the server.
- Never sync before answering a notification. Graph wants a reply within 3 seconds and drops the notifications of a slow endpoint; the handler verifies, answers 202 and hands the sync to `defer`, with the cron as the safety net. The webhook test checks that nothing has synced when the response returns, and the routes test that syncs go to `defer`.
- Never trust a notification before its clientState verifies and names the subscription it arrived on. The endpoint is public, so the signed clientState is the only credential. The rules suite checks the signature binds to its subscription, and the webhook test sends forged, foreign and unknown notifications that must schedule nothing.
- Never store tokens in clear text. A refresh token is long-lived read and write access to a calendar, so both tokens are sealed with AES-256-GCM before they reach the store. The rules suite round-trips the encryption and the connect test checks the stored tokens are sealed.
- Never let an Outlook failure fail a booking. The booking store is the truth about bookings; outbound writes log the error and return, and the booking stands. `pushBookingEvent` and `removeBookingEvent` catch at their boundary; this rule is checked by reading, as no test yet throws through them.

Not this:
- Google Calendar busy time and booking events: `google-calendar-sync`
- Generating bookable slots and subtracting busy time: `time-slot-generator`
- Public booking pages per host, Calendly-style: `calendar-scheduling-links`
- Signing users in with Microsoft (SSO): The host app's identity provider
- Room and resource mailboxes, organisation-wide access without per-user consent: Application permissions with admin consent, a different design
- Exchange Server on-premises: EWS, not Graph

What to tell your agent:
- Let hosts in this Next.js app on Supabase connect their Microsoft 365 or Outlook calendar: the times they are busy in Outlook must stop being bookable, and every confirmed booking should show up in their Outlook calendar and disappear again when it is cancelled.
- Add Outlook calendar sync to our Firestore backend, and put each booking into the host's calendar as a Teams meeting with the guest invited.
- Our Outlook calendar integration stops working about an hour after a host connects, and their vacations never block anything. Find out why and fix it.

Agent eval results (newest per agent and prompt):
- Gemini CLI (gemini-3.8-flash) on v0.1.4, 2026-10-16: pass (Supabase)
- Codex CLI (gpt-6.1-sol) on v0.1.4, 2026-10-16: pass (Supabase)
- Claude Code (claude-opus-5-5) on v0.1.4, 2026-10-16: pass (Supabase)

Recent releases:
- v0.1.4 (2026-10-16): From scoring the prompt-1 agent eval runs against 0.1.3. Wording only; no template changed.
- v0.1.3 (2026-10-16): From scoring the prompt-1 agent eval runs against 0.1.2. Wording only; no template changed.
- v0.1.2 (2026-10-16): From scoring the prompt-1 agent eval runs against 0.1.1. Wording only; no template changed.
