Case study · Systems · 2026
Adding Outlook to a booking system without making Outlook the booking system
Adding Outlook availability to Connection Clinic sounded like a fairly contained feature.
A therapist defines the hours they normally work. They connect their Microsoft 365 account. If something is already in their Outlook calendar, Connection Clinic doesn't offer that time to a client.
Simple enough.
Except the interesting problem wasn't connecting to Microsoft.
It was deciding which system was allowed to believe what.
We already had a booking system
Connection Clinic already had its own scheduling logic.
Therapists could define availability, associate services with it, and offer services of different durations. The system generated suitable appointment times, applied booking-window rules, prevented conflicts, held appointments during checkout and took payment through Stripe.
So when we came to add Outlook, I didn't want to build a second scheduling system.
That would have left us with two competing versions of a therapist's working life: what Connection Clinic thought was available and what Microsoft thought was available.
Instead, I settled on a fairly simple rule:
Connection Clinic owns bookings. Outlook informs availability.That distinction ended up shaping almost everything that followed.
Outlook only needs to answer one question
The first version of the calendar integration was deliberately narrow.
Connection Clinic doesn't need to know that a therapist has Dentist in their calendar at 11am, or who they're meeting at 2pm, or what they've written in the appointment notes.
It needs to know they're busy.
So rather than letting Microsoft-specific concepts leak throughout the booking system, I introduced an external-calendar layer that effectively answers:
When is this therapist busy?Microsoft's calendar data is converted into generic busy periods before it reaches the scheduling logic.
That also creates a useful privacy boundary. The booking engine doesn't need Outlook event subjects, descriptions, attendees or other private calendar content. It deals with time.
And because the rest of Connection Clinic isn't built around Microsoft Graph, another calendar provider could eventually answer the same question without requiring the booking engine to be rewritten.
Working hours minus busy time
The next problem was turning those busy periods into actual appointments.
Each therapist can now define their normal working pattern: their timezone, the days and hours they work, and how frequently appointments can start.
Service duration remains separate.
That's important.
A therapist might allow appointments to start every 30 minutes while offering both 60-minute and 90-minute services. A 30-minute interval doesn't mean a 30-minute appointment; it just determines the possible starting points.
Once Outlook is connected, the basic calculation becomes:
normal working hours − Outlook busy periods = free windowsThose free windows then go into the same appointment-generation machinery Connection Clinic already used.
So, broadly:
working hours
→ subtract Outlook busy periods
→ free windows
→ eligible services
→ service duration + start interval
→ possible appointments
→ existing booking conflicts
→ booking-window rules
→ /bookThe useful thing about this model is what didn't change.
Outlook doesn't generate appointments.
The existing Connection Clinic booking engine does.
I didn't save Outlook availability to the database
It was tempting to convert those free periods into normal Connection Clinic availability records.
I decided against it.
Outlook-derived availability is calculated when it's needed and kept in memory. We don't fill the database with artificial availability records representing what somebody's calendar happened to look like at an earlier point in time.
That keeps the distinction fairly clean.
A therapist's working hours are policy.
Outlook tells us what currently interrupts those hours.
Connection Clinic calculates what can actually be booked.
Manual availability created another decision
Connection Clinic already supported manually created availability, so connecting Outlook introduced an awkward question.
Should both operate at once?
I decided they shouldn't.
A therapist is either using manual availability or calendar-managed availability.
When no external calendar is connected, their existing manual availability works as before.
When Outlook is connected, those manual records aren't deleted. They're preserved, but suppressed from public booking while Outlook is the active availability source.
That matters when something goes wrong too.
If Microsoft's API is unavailable, or the therapist needs to reconnect their account, Connection Clinic doesn't quietly fall back to old manual availability.
It fails closed.
In other words, uncertainty produces fewer appointments, not more.
I'd much rather temporarily show no availability than offer a client 2pm because an external dependency failed and we decided to assume the therapist was free.
Seeing a free appointment doesn't mean it's still free
There's another problem with calendar-derived availability that isn't immediately obvious.
Imagine you open Connection Clinic at 10:00 and it tells you Tuesday at 3pm is available.
At 10:01, somebody puts something into the therapist's Outlook calendar for Tuesday at 3pm.
At 10:02, you click the appointment you already have on screen.
The booking page can't simply trust what it showed you two minutes earlier.
Connection Clinic already used signed booking slots rather than trusting appointment details submitted by the browser. Calendar-derived appointments were brought into that same architecture.
When somebody proceeds with one, the server regenerates and revalidates the opportunity.
And immediately before accepting an Outlook-backed booking, Connection Clinic checks Outlook again.
If the time is still free, it can secure the corresponding Connection Clinic hold and create the provisional Outlook event before continuing into checkout.
That means the calendar is checked at the point where the answer actually matters, not just when the appointments were first displayed.
The project also added a durable exact-slot claim to help protect against duplicate attempts at the same external-calendar slot.
Then we write back to Outlook
Reading Outlook solves half the problem.
Once Connection Clinic accepts an appointment, Outlook needs to know about that too. Otherwise the same therapist could appear free to somebody looking directly at their calendar.
So an Outlook-backed booking creates an Outlook event as part of the booking lifecycle.
Importantly, this happens before taking the client into Stripe Checkout.
If the Outlook event can't be created safely, the Connection Clinic hold can be released rather than accepting payment for an appointment we couldn't properly secure in the external calendar.
If checkout is abandoned and the pending hold later expires, Connection Clinic can clean up the provisional Outlook event as part of releasing it.
This is where a seemingly simple calendar integration starts becoming less about calendars and more about state.
Outlook still doesn't own the appointment
Writing appointments to Outlook raises another question.
What happens if the therapist changes that event directly in Outlook?
The answer comes back to the original rule.
Connection Clinic owns bookings.
Deleting the Outlook event doesn't cancel the client's booking.
Dragging it from 3pm to 4pm doesn't silently reschedule the client's appointment.
Those actions make Outlook inconsistent with Connection Clinic; they don't rewrite the booking.
For a missing event, Connection Clinic can identify the discrepancy and recreate its calendar representation.
Cancellation works in the other direction. The booking is cancelled in Connection Clinic first. Outlook cleanup follows.
If Microsoft is temporarily unavailable and the event can't be deleted, the booking doesn't magically remain active. Connection Clinic records the sync problem so it can be dealt with without surrendering authority over the actual appointment.
That distinction sounds small, but I think it's the most important decision in the whole integration.
Without it, two systems can both change the same thing and neither one is clearly correct.
We deliberately didn't solve everything
There's always a temptation when integrating a calendar to keep expanding the feature.
Full two-way rescheduling would be useful.
Google Calendar would be useful.
Background retry queues and continuous reconciliation could make recovery more automatic.
But none of those things were required to solve the immediate problem.
The goal was to let a therapist connect Outlook, define when they normally work, and have existing commitments automatically removed from what Connection Clinic offers clients.
So that's what I built.
The integration grew incrementally: scheduling policy first, then a provider abstraction, then Microsoft authentication and free/busy, then public availability, then write-back and concurrency protection, and finally lifecycle and recovery behaviour.
That sequence made it possible to keep testing the existing booking system rather than replacing a working architecture in one big leap.
The calendar API wasn't the hard bit
Looking back, Microsoft authentication, tokens and API calls were obviously necessary, but they weren't really the architectural problem.
The harder questions were things like:
- Who owns the booking?
- What happens when systems disagree?
- What should we do when an external service is unavailable?
- What information do we actually need to import?
- When does apparently free time become a genuinely claimed appointment?
Those aren't Outlook questions.
They're system-design questions.
And the answer I kept coming back to was to make the external system responsible for as little as possible.
Outlook tells Connection Clinic when somebody is busy.
Connection Clinic decides what can be booked.
And Connection Clinic remains the place where the booking actually exists.
Next
Why we built our own booking platform
How a limitation in third-party software led Connection Clinic to build its own therapist booking platform.