Scheduling & Appointments — How it works
Overview
The scheduling engine (M13-001…003) owns provider and service calendars and the appointment lifecycle. An administrator defines a schedule template — the weekly pattern a provider or a facility service works — and schedule blocks carve absences out of it. Bookable slots are then computed on read from template minus blocks minus existing appointments, and an appointment is booked into one. Arrival at the desk hands the patient over to the clinical and workflow engines.
Data model & ownership
| Table | Purpose |
|---|---|
schedule_template | A weekly pattern for one provider_id or one facility_service_id; slot duration, capacity, effective-from/to. |
schedule_template_day | One working window (day_of_week, start_time, end_time) on a template. |
schedule_block | An absence: a provider's leave, or (with no provider) the whole facility closed for a window. Carries a mandatory reason. |
appointment | The booking: patient, optional provider/service, starts_at/ends_at, relational status, appointment_type_id. |
Migrations: scheduling/001 (templates + blocks), scheduling/002 (appointments),
scheduling/003 (appointment visit type).
There is no slot table, by design. A stored slot is a second source of truth that drifts from
the template the moment anybody edits one, and a year of empty slots for every provider is a table
nobody reads. AvailabilityQueryService computes them per query instead.
Key rules & invariants
appointment.appointment_type_idis avisit_typeid. It is the kind of visit that was booked, and it is what arrival opens — and therefore what decides which patient journey the patient walks (visit_type_journey_mapping). A null falls through to the clinical engine's default rather than inventing intent for appointments booked before the column existed.- Status is never taken from the request. Every appointment starts
BOOKED; a client that could postCOMPLETEDwould be writing a clinical event that never happened. Statuses areBOOKED,CONFIRMED,CHECKED_IN,COMPLETED,CANCELLED,NO_SHOW; the first four occupy slot capacity, and the last three are terminal (arrival and cancellation refuse them). - Arrival is idempotent and does not touch the queue.
arriveresolves the visit throughEncounterContextService("the named visit, else the patient's open one here, else open one"), marks the appointmentCHECKED_IN, and stops. The workflow engine starts the journey and the M28-003 bridge does the enqueueing. A receptionist clicking twice must not open a second stay. - Emergency cover at arrival (M13-003B): the booked provider can be swapped at check-in, and the replacement is refused if they are already blocked or booked for that window.
- Blocking is refused over live appointments. Creating a block across a window that already has occupying appointments fails rather than silently stranding patients.
- Booking is patient-scoped.
book()consults the patient access guard — holding the booking permission says nothing about whether this caller may act on this patient. ⚠ It currently callsauthorizerather thanauthorizeOrNotFound, so a denial answers 403 where an unknown patient answers 404. That asymmetry is an existence oracle and is a known open item.
API
See the API Reference. Endpoints under /api/v1/scheduling:
| Verb | Path |
|---|---|
| POST / GET / PUT / DELETE | /schedule-templates[/{id}] |
| POST / GET / DELETE | /schedule-blocks[/{id}] |
| GET | /availability?providerId=&from=&to= — computed slots |
| POST / GET | /appointments (search by provider, patient, date range) |
| POST | /appointments/{id}/arrive[?providerId=] — check-in |
| POST | /appointments/{id}/cancel?reason= |
| POST | /appointments/{id}/reschedule |
Note that arrive and cancel take query parameters, not a body.
Permissions
Declared on SchedulingModuleDescriptor so the catalog seeder registers them:
scheduling.template.read / .write, scheduling.calendar.read,
scheduling.appointment.read / .write / .manage.
Configuration & feature flags
Domain-module Feign client exchange.client.scheduling.SchedulingClient; URL property
scheduling.service.url (SCHEDULING_SERVICE_URL).
Related features
- Clinical — arrival resolves the visit here.
- Workflow — the visit type maps to the journey the patient then walks.
- Queue & waiting board — where the patient becomes visible after check-in.
- User guide: Configure triage for your facility.