Spike Microsoft Bookings as primary external booking engine before Cal.com self-host deployment
ADR-009: Spike Microsoft Bookings as primary external booking engine before Cal.com self-host deployment
Status: Rejected
Date: 2026-04-17 proposed, 2026-04-23 spike executed and rejected.
Deciders: Mishaal Murawala
Spike findings: docs/spikes/ms-bookings-2026-04-23.md
Relates to: ADR-001 (Graph-first scheduling), ADR-008 (cal.diy rejected, Cal.com retained), ADR-019 (Scheduler 2-5 deferred), docs/prd/smart-scheduler-v5-implementation.md §4–§5
Rejection (2026-04-23)
The spike ran as documentation research per the Phase 1 Task 1.4 constraint in ADR-018 (no new tools, no Scheduler 2-5 code during Phase 1).
Dealbreaker tripped: D1 — Webhook coverage is incomplete. The Microsoft Graph change-notifications supported-resources table does not list any Bookings resource (bookingAppointment, bookingBusiness, bookingService, bookingStaffMember, bookingCustomer). Not on v1.0. Not on beta. This makes the scheduler v5 PRD §5 event-driven flows (Slack notify, CRM log on confirmation, reminder sequencing, auto-rebook on cancel) unimplementable without polling, which is an architectural regression.
A secondary dealbreaker (D4 — branding) is also likely tripped: the public scheduling page URL is outlook.office.com/bookwithme/..., confirmation emails use fixed Microsoft templates, and bookingPageSettings exposes only color + logo + consent text — no custom domain, no CSS override, no email-template branding. Adequate for internal Ascend use, borderline for PFP portfolio clients who need branded booking experiences.
Remaining acceptance criteria evaluate favorably (public page + programmatic provisioning + multi-advisor + services/buffers + customer-managed reschedule via isCustomerAllowedToManageBooking), but the webhook gap alone is disqualifying.
Full findings and the per-criterion evaluation are in the spike doc linked above.
Status transitions: Proposed (2026-04-17) → Rejected (2026-04-23). Supersedes nothing; ADR-001’s Cal.com selection remains canonical for whenever ADR-019 is reversed.
Context
ADR-001 committed V5 to Microsoft Graph for internal and Cal.com for external booking. That ADR did not evaluate Microsoft Bookings — Microsoft’s own external self-serve booking product, which is:
- Free with any M365 Business Standard+ license (PFP already pays for this).
- Native to Graph API via
/solutions/bookingBusinessesendpoints. - Supports public booking pages, staff pools, service types, buffer times, automated email confirmations, Teams meetings, cancellation links.
- Zero added vendor, zero added ops footprint (no Postgres, Redis, Docker, TLS, domain).
V5 already authenticates against Graph for findMeetingTimes. The token + OAuth infrastructure to call Bookings is already built. This was a gap in ADR-001’s options matrix.
Per ADR-008, Cal.com self-host is licensing-clean (MIT). But it still costs us ops surface. If Bookings covers the PFP external-booking use case, Cal.com comes out of V5 entirely.
Decision (Proposed)
Run a 1-day spike to evaluate Microsoft Bookings against the scheduler v5 external-booking requirements. If the spike passes defined acceptance criteria, supersede the Cal.com decision in ADR-001 and remove Cal.com from the V5 build scope. If it fails, proceed with Cal.com self-host deployment as currently specced.
Spike Plan (1 day)
Acceptance Criteria
MS Bookings must support, verified via API:
- Public booking page per advisor — shareable URL an external invitee can visit without an M365 account.
- Multi-advisor tenant — one booking business per tenant, multiple staff (advisors).
- Event-type equivalent — distinct “services” with durations, buffers, custom questions.
- Programmatic creation — create/update booking businesses, services, staff entirely via Graph API (no admin-portal-only steps).
- Webhook on booking — receive an event when an external user books, so V5 can trigger follow-up flows (Slack notify, CRM log, etc.).
- Cancellation + reschedule links in confirmation emails.
- Branding — sufficient to not look like a Microsoft-template page (logo, colors, business name).
Dealbreakers (any one → reject, fall back to Cal.com)
- Webhook coverage is incomplete (Graph subscriptions don’t cover
bookingAppointmentcreation). - No programmatic way to provision new booking businesses (i.e. must click through admin center per tenant).
- Can’t expose a public booking page without assigning the invitee an M365 license.
- Booking page can’t be branded enough to use for PFP portfolio clients.
Tasks
- Create a
spike/ms-bookingsbranch. - Provision a test booking business via Graph on Mishaal’s M365 tenant (Kahuna).
- Create 1 service + 2 staff members via API.
- Publish the booking page, book an appointment as an “external” user (incognito, no M365 login).
- Attempt to subscribe to
bookingAppointmentchange notifications via Graph webhook. - Test cancellation/reschedule flow from the confirmation email.
- Document findings in
docs/spikes/ms-bookings-YYYY-MM-DD.mdagainst the acceptance criteria above. - Decide: supersede ADR-001 Cal.com decision, OR close the spike and proceed with Cal.com.
Out of Scope for the Spike
- Multi-tenant provisioning automation (punt until primary decision is made).
- Custom branding beyond what Bookings offers natively (if branding is a dealbreaker, we’ll know from the spike).
- Performance/load testing.
Consequences (if MS Bookings wins)
Positive
- No Postgres, Redis, Docker, domain, or TLS cert for Cal.com.
- No third-party vendor in the external booking path (aligns with V5 invariant #4 spirit — “no external vendors in the token path” extended to “minimize vendors across the board”).
- PFP is M365-native; Bookings feels like part of their own tenant, not a bolt-on.
- Scheduler v5 spec §5 Phase 4 (“Cal.com Integration”) becomes a Bookings integration — roughly equivalent LOC, simpler ops.
Negative
- Bookings UX is less polished than Cal.com.
- If we ever onboard a Google-Workspace-only tenant, we’d need a second external engine (Cal.com would come back).
- We lose Cal.com’s routing forms, round-robin collective events, and marketplace integrations — need to check if PFP actually uses these.
Consequences (if MS Bookings loses)
- Proceed with Cal.com self-host as specced. No wasted time beyond the 1-day spike.
- Spike doc becomes the justification for the Cal.com investment (we evaluated and MS Bookings didn’t meet bar).
Follow-ups
-
CreateSpike run as documentation research — no branch needed per Phase 1 Task 1.4 constraint.spike/ms-bookingsbranch. - Execute the spike plan as documentation research against current Graph docs (2026-04-23).
- Write
docs/spikes/ms-bookings-2026-04-23.md. - Mark this ADR
Rejectedwith spike doc as justification (this PR). -
Deploy Cal.com self-host per ADR-001.Blocked by ADR-019: Scheduler Phases 2-5 are deferred with no target date. Cal.com deployment re-opens only when ADR-019’s reversal criteria fire (paying client ask + named functionality, or usage threshold, or downstream ADR dependency).
References
- Microsoft Graph Bookings API: https://learn.microsoft.com/en-us/graph/api/resources/booking-api-overview
- Scheduler v5 spec:
docs/prd/smart-scheduler-v5-implementation.md - ADR-001:
docs/decisions/ADR-001-graph-first-scheduling.md - ADR-008:
docs/decisions/ADR-008-external-booking-engine-revisit.md