Partners read availability. They never become the calendar.
A vendor-neutral public contract already exists. Practice websites consume it, and staff approve every request. BookingRequest is not an Appointment.
Two public resources. One engine.
Every calculation flows through ISchedulingAvailabilityService in SchedulingService. There is no parallel availability engine. A channel adapter — including a Zocdoc adapter — plugs in over the same service.
GET /api/public/v1/availability
JSON slots
Practice timezone (for example America/Phoenix). Zone-offset start and end. durationMinutes. Each slot carries an opaque, encrypted availabilityToken. Query by public slugs — providerId, locationId, appointmentTypeId — not database ids.
GET /api/public/v1/availability.ics
iCalendar feed
The same free time as the JSON resource. Only free and bookable slots are exposed. A booked or blocked period simply yields no slot.
POST returns 202. Staff approve later.
POST /api/public/booking-requests
Authorization: Bearer # or X-Api-Key
{
"name": "Jane Doe",
"phone": "480-555-0100",
"email": "[email protected]",
"patientRelationship": "Existing",
"preferredStart": "2026-08-20T21:00:00Z",
"durationMinutes": 60,
"reason": "New patient exam",
"availabilityToken": ""
}
→ 202 Accepted { "status": "accepted", "eventId": "…" }
Revalidation
Submitting a token re-runs the engine server-side. If the slot was consumed, the API returns 409 Conflict. Previously loaded availability is never treated as authoritative.
No PHI read path
IntakeService validates and publishes BookingRequestedEvent. It cannot read appointments or patient records. It stores only a minimized durable inbox row.
Staff remain the calendar
SchedulingService consumes the event and creates a BookingRequest. Staff match or create the patient, then approve and schedule — or reject.
The website keeps working.
The topic decouples the website from the scheduler.
| Condition | Intake response | What happens |
|---|---|---|
| Public booking disabled | 404 | Default. Nothing is internet-reachable until you turn it on. |
| Missing or wrong API key | 401 | Compared in constant time. Bearer or X-Api-Key. |
| Rate limit | 429 | Fixed window, 5 requests per minute per client IP. Forwarded addresses honored only from TrustedProxies. |
| Stale slot | 409 | Caller reloads availability. No silent overwrite. |
| SchedulingService down | 202 | Event waits in the scheduling subscription. No booking is lost. The website is unaffected. |
| Service Bus unreachable | 503 | No false 202. The calling website falls back to its own path (email). |
A channel adapter on the same engine.
The Zocdoc integration is built on Zocdoc’s scheduling API, and Zocdoc has agreed to a pilot starting in October 2026. Open times are published to Zocdoc from the same availability engine, so it never becomes a second calendar. Zocdoc bookings arrive by signed webhook, are re-checked against the calendar, and are confirmed without a staff approval step. That is Zocdoc’s confirmed-booking model, and it differs from website requests. Zocdoc receives open times, never the practice’s patient records.
The first reference integration is documented on the Pilot page. No practice name or internal patient, provider, or location identifier is coupled to the intake workflow. CORS is not required — the website calls IntakeService server-to-server.
From first call to a scoped channel.
Evaluate the source yourself. Then we scope a public-intake channel for one practice.
1 · Align on scope
We map the practice calendar, website, and any marketplace channel. We agree what IntakeService will expose — and what it will not.
1–2 weeks
2 · Enable public intake
API key, rate limit, practice timezone, booking horizon. Disabled by default until you turn it on. The website keeps an email fallback.
1–2 weeks
3 · Pilot a channel
Website booking requests land in the portal for staff to review and approve. A marketplace channel such as Zocdoc publishes from the same availability engine and confirms its bookings after re-checking the slot, with no staff approval step. Either way, the calendar remains the practice’s.
Ongoing
Inspect the intake service. Then talk about a channel.
Disabled by default. API-key gated. Isolated from PHI.