Skip to content

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/bookingBusinesses endpoints.
  • 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:

  1. Public booking page per advisor — shareable URL an external invitee can visit without an M365 account.
  2. Multi-advisor tenant — one booking business per tenant, multiple staff (advisors).
  3. Event-type equivalent — distinct “services” with durations, buffers, custom questions.
  4. Programmatic creation — create/update booking businesses, services, staff entirely via Graph API (no admin-portal-only steps).
  5. Webhook on booking — receive an event when an external user books, so V5 can trigger follow-up flows (Slack notify, CRM log, etc.).
  6. Cancellation + reschedule links in confirmation emails.
  7. 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 bookingAppointment creation).
  • 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

  1. Create a spike/ms-bookings branch.
  2. Provision a test booking business via Graph on Mishaal’s M365 tenant (Kahuna).
  3. Create 1 service + 2 staff members via API.
  4. Publish the booking page, book an appointment as an “external” user (incognito, no M365 login).
  5. Attempt to subscribe to bookingAppointment change notifications via Graph webhook.
  6. Test cancellation/reschedule flow from the confirmation email.
  7. Document findings in docs/spikes/ms-bookings-YYYY-MM-DD.md against the acceptance criteria above.
  8. 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

  • Create spike/ms-bookings branch. Spike run as documentation research — no branch needed per Phase 1 Task 1.4 constraint.
  • 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 Rejected with 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