# Integration — Microsoft Graph

> Two **separate** Azure apps: Bookings (meetings) and Teams Webinars (demos). They must never share credentials.

_Verified against `config/microsoft_bookings*.php`, `config/demo_webinars.php`, and `app/Services/Microsoft/*` on 2026-07-23._

---

## A. Bookings — meeting scheduling
- **Auth:** app-only client credentials (`MS_GRAPH_TENANT_ID/CLIENT_ID/CLIENT_SECRET`), scope `Bookings.ReadWrite.All`.
- **Config:** `config/microsoft_bookings.php` (+ `..._staff.php`). Per-flow, **strictly separated** investor vs startup: `business_id` (Bookings mailbox), `service_id`, `staff_ids`, service name, duration, lead hours. Availability cache TTLs; `client_state` echoed on webhooks.
- **Services:** `GraphTokenProvider` (app token), `StaffDirectory`, `BookingsSubscriptionManager`, `SlotCalculator`.
- **Flow:** native Laravel UI → `/api/bookings/{days,availability,create}` → `Meeting` created; `webhooks/microsoft-bookings` syncs status/times into the `meetings` table. Master toggle `MS_BOOKINGS_ENABLED`.

## B. Teams Webinars — demo days
- **Dedicated, isolated Azure app** — **must not** read the Bookings credentials.
- **Auth:** delegated OAuth (authorization code + `offline_access`), scopes incl. `VirtualEvent.ReadWrite`, `User.Read`. Silent refresh.
- **Config:** `config/demo_webinars.php` — `DEMO_WEBINARS_ENABLED` **off by default**; dedicated `WEBINAR_GRAPH_*` app; organizer fixed. `GraphOAuthToken` persists the delegated token.
- **Services:** `DelegatedGraphTokenProvider` (delegated token, exposed via MCP `webinar_connection_*` tools), `WebinarProbe`, `WebinarAppTokenProvider` (app-only for the registration surface).
- **Flow:** admin connects via OAuth (`/admin/.../webinars` connection) → `DemoWebinar` created (draft → published) → registrations + attendance sync.

## Hard rule
Bookings (app-only) and Webinars (delegated, separate app) are **isolated**. There is no fallback that could cross-wire them.
