Dev Reference

Scheduling

Resource-graph scheduling with multi-stage visit types, provider/room/equipment constraints, and waitlist automation.

Data Classification: All 7 entities are org-scoped. Partition key: OrganizationID. No cross-org data sharing. See Data Model for ownership conventions.

Schedule P0

A named block of availability for a provider at a location.

Schema

FieldTypeRequiredPKFKNotes
ScheduleIDUUIDYesYes
ProviderIDUUIDYesProvider
LocationIDUUIDYesLocation
ScheduleNameString(200)Yes
ScheduleKindString(50)YesInPerson | Telehealth | Hybrid | GroupVisit
EffectiveStartDateTimeUTCYes
EffectiveEndDateTimeUTCNoNull = open-ended
IsActiveBoolYes
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)Read public schedule metadata
FrontDesk (30)Read
MA / RN (40)Read
Provider (50)Read & edit own schedules
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

Slot P0

A discrete time segment on a schedule, governed by optimistic concurrency via ResourceVersion.

Schema

FieldTypeRequiredPKFKNotes
SlotIDUUIDYesYes
ScheduleIDUUIDYesSchedule
VisitTypeIDUUIDYesVisitType
SlotStartDateTimeUTCYes
SlotEndDateTimeUTCYes
SlotStatusString(50)YesFree | Held | Busy | Cancelled
HoldExpiresDateTimeUTCNoTTL for Held status
ResourceVersionIntYesOptimistic concurrency vector
OverbookAllowedBoolYes
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)Read self-bookable slots only
FrontDesk (30)Read & book (Hold/Busy transitions)
MA / RN (40)Read
Provider (50)Read
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

Appointment P0

The core booking record linking a patient to a slot. Tracks status through a full lifecycle with reminder-ladder automation and reschedule chains.

Schema

FieldTypeRequiredPKFKNotes
AppointmentIDUUIDYesYes
SlotIDUUIDYesSlot
PatientIDUUIDYesPatient
ProviderIDUUIDYesProvider
VisitTypeIDUUIDYesVisitType
EligibilityCheckIDUUIDNoEligibilityCheckFrom Eligibility module
RescheduledFromAppointmentIDUUIDNoAppointment (self)Reschedule chain link
AppointmentStatusString(50)YesBooked | Confirmed | CheckedIn | InProgress | Completed | NoShow | Cancelled | Rescheduled
BookingChannelString(50)YesFrontDesk | Portal | Phone | WalkIn | WaitlistOffer
EligibilityStatusString(50)NoPending | Active | Inactive | Unknown
CheckedInDateTimeUTCNo
RoomedDateTimeUTCNo
CompletedDateTimeUTCNo
NoShowMarkedDateTimeUTCNo
CancellationReasonString(500)No
ReminderLadderStateString(200)NoJSON: tracks SMS/email/voice reminder cadence
IsTelehealthBoolYes
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)Read & cancel own appointments
FrontDesk (30)Full CRUD on org's appointments
MA / RN (40)Update status (CheckedIn, Roomed, etc.)
Provider (50)Read & edit own appointments
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

Resource P0

Any schedulable asset: providers, rooms, equipment, or telehealth bridges. Resources carry capability tags for constraint matching.

Schema

FieldTypeRequiredPKFKNotes
ResourceIDUUIDYesYes
LocationIDUUIDYesLocation
ProviderIDUUIDNoProviderOptional; set when ResourceKind = Provider
UserIDUUIDNoUserOptional; links to IAM user
ResourceKindString(50)YesProvider | RN | MA | ExamRoom | Equipment | FrontDesk | TelehealthBridge | WaitingRoom
ResourceNameString(200)Yes
CapabilityTagsTextNoComma-delimited (e.g. "spirometry,ekg,phlebotomy")
CapacityIntYes1 for rooms; >1 for group/waiting
IsActiveBoolYes
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)None
FrontDesk (30)Read
MA / RN (40)Read
Provider (50)Read
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

VisitType P0

Template defining duration, eligibility requirements, and self-scheduling rules. Stages break each visit into granular resource demands.

Schema

FieldTypeRequiredPKFKNotes
VisitTypeIDUUIDYesYes
VisitTypeNameString(200)Yes
VisitTypeCodeString(50)Yes
TotalDurationMinutesIntYesDenormalized sum of stages
RequiresEligibilityCheckBoolYes
RequiresPriorAuthBoolYes
SelfSchedulableBoolYesExposed to Patient Portal
NewPatientAllowedBoolYes
DefaultProviderRoleString(50)NoMD | DO | NP | PA | RN | MA
IsActiveBoolYes
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)Read self-schedulable visit types only
FrontDesk (30)Read
MA / RN (40)Read
Provider (50)Read
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

VisitStage P0

An ordered step within a visit type. Each stage declares which resource kinds and capabilities it requires, enabling the resource-graph solver to allocate rooms and staff.

Schema

FieldTypeRequiredPKFKNotes
VisitStageIDUUIDYesYes
VisitTypeIDUUIDYesVisitType
StageOrderIntYes1-based ordering
StageNameString(50)YesCheckIn | Waiting | Rooming | ProviderEval | NurseFollowUp | Checkout | Procedure | Telehealth
DurationMinutesIntYes
RequiredResourceKindsTextNoComma-delimited list of Resource.ResourceKind values
RequiredCapabilityTagsTextNoComma-delimited; matched against Resource.CapabilityTags
HoldsRoomFromPriorStageBoolYesIf true, exam room stays reserved from previous stage
IsParallelizableBoolYesCan overlap with prior stage
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)Read self-schedulable visit types only
FrontDesk (30)Read
MA / RN (40)Read
Provider (50)Read
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

WaitlistEntry P1

A patient's request for an earlier or preferred slot. The system scores, offers, and expires entries automatically.

Schema

FieldTypeRequiredPKFKNotes
WaitlistEntryIDUUIDYesYes
PatientIDUUIDYesPatient
VisitTypeIDUUIDYesVisitType
PreferredProviderIDUUIDNoProviderOptional preference
OfferedSlotIDUUIDNoSlotSet when a slot is offered
EarliestAcceptableDateTimeUTCYes
LatestAcceptableDateTimeUTCYes
NotifyChannelString(50)YesSms | Email | Both
WaitlistStatusString(50)YesActive | Offered | Accepted | Expired | Withdrawn
OfferExpiresDateTimeUTCNoTTL for offer acceptance
PriorityScoreIntYes0-100; higher = more urgent
OrganizationIDUUIDYesOrganizationPartition key
CreatedByUserIDUUIDYesUserAudit
CreatedDateTimeUTCDateTimeUTCYesAudit
UpdatedByUserIDUUIDYesUserAudit
UpdatedDateTimeUTCDateTimeUTCYesAudit
IsDeletedBoolYesSoft-delete

RBAC

Role (Tier)Access
Patient (10)Own entries only
FrontDesk (30)Full CRUD on org's entries
MA / RN (40)Read
Provider (50)Read
PracticeMgr (70)Full CRUD
EditAMI (90+)Schema management

FK Relationship Diagram

erDiagram
    Provider ||--o{ Schedule : "owns"
    Location ||--o{ Schedule : "hosts"
    Schedule ||--o{ Slot : "contains"
    VisitType ||--o{ Slot : "typed by"
    Slot ||--o{ Appointment : "books into"
    Patient ||--o{ Appointment : "attends"
    Provider ||--o{ Appointment : "sees"
    VisitType ||--o{ Appointment : "typed by"
    EligibilityCheck ||--o| Appointment : "verifies"
    Appointment ||--o| Appointment : "rescheduled from"
    Location ||--o{ Resource : "houses"
    Provider ||--o| Resource : "linked to"
    User ||--o| Resource : "linked to"
    VisitType ||--o{ VisitStage : "broken into"
    Patient ||--o{ WaitlistEntry : "requests"
    VisitType ||--o{ WaitlistEntry : "for type"
    Provider ||--o| WaitlistEntry : "prefers"
    Slot ||--o| WaitlistEntry : "offered"
  

Resource-Graph Scheduling Model

The scheduling engine uses a constraint-satisfaction approach. Each VisitStage declares required resource kinds and capability tags. The solver finds a feasible allocation of resources across all concurrent stages in the time window, respecting capacity limits and provider availability.

graph TD
    VT[VisitType] --> VS1[Stage 1: CheckIn]
    VT --> VS2[Stage 2: Rooming]
    VT --> VS3[Stage 3: ProviderEval]
    VT --> VS4[Stage 4: Checkout]
    VS1 -->|requires| R_FD[FrontDesk Resource]
    VS2 -->|requires| R_MA[MA Resource]
    VS2 -->|requires| R_RM[ExamRoom Resource]
    VS3 -->|requires| R_MD[Provider Resource]
    VS3 -->|holds room| R_RM
    VS4 -->|requires| R_FD
  

Constraint Rules (12)

#RuleTypeDescription
1Room capacityHardExam room occupancy must not exceed Resource.Capacity.
2Provider availabilityHardAppointment must fall within an active Schedule for the provider.
3MA/RN concurrencyHardMA or RN cannot be double-booked beyond their Capacity value.
4Equipment dependencyHardStages requiring equipment (e.g. spirometry) must have an available Equipment resource with matching CapabilityTag.
5Capability-tag matchHardVisitStage.RequiredCapabilityTags must be a subset of assigned Resource.CapabilityTags.
6Lunch / block scheduleHardSlots cannot be created during provider block-time windows (lunch, admin, etc.).
7Telehealth bridge capacityHardConcurrent telehealth sessions must not exceed TelehealthBridge.Capacity.
8New-patient gatingHardIf VisitType.NewPatientAllowed = false, new patients are rejected.
9Eligibility preconditionSoftIf VisitType.RequiresEligibilityCheck = true, warn when no active check exists. Does not block booking.
10Wait-time SLASoftFlag appointments where projected wait exceeds practice-defined SLA (default 15 min).
11Reschedule chain lengthSoftWarn when RescheduledFromAppointmentID chain exceeds 3 hops.
12Concurrency / version vectorHardSlot updates must pass optimistic concurrency check on ResourceVersion.

Appointment State Machine

stateDiagram-v2
    [*] --> Booked
    Booked --> Confirmed : Patient confirms (ReminderLadder)
    Booked --> Cancelled : Patient/staff cancels
    Booked --> Rescheduled : Reschedule requested
    Booked --> NoShow : Missed without cancel
    Confirmed --> CheckedIn : Front-desk / kiosk check-in
    Confirmed --> Cancelled : Late cancel
    Confirmed --> Rescheduled : Reschedule requested
    Confirmed --> NoShow : Missed without cancel
    CheckedIn --> InProgress : Roomed by MA/RN
    InProgress --> Completed : Provider signs off
    Rescheduled --> Booked : New appointment created
    NoShow --> [*]
    Cancelled --> [*]
    Completed --> [*]
  

Functional Requirements

  1. P0 The system shall create, read, update, and soft-delete Schedules per provider per location with effective date ranges.
  2. P0 The system shall auto-generate Slots from a Schedule template, respecting block-time windows and VisitType durations.
  3. P0 The system shall enforce optimistic concurrency on Slot updates via ResourceVersion to prevent double-booking.
  4. P0 The system shall transition Appointment status through the full state machine (Booked, Confirmed, CheckedIn, InProgress, Completed, NoShow, Cancelled, Rescheduled).
  5. P0 The system shall fire an eligibility check when VisitType.RequiresEligibilityCheck is true and surface the result on the Appointment.
  6. P0 The system shall support the resource-graph solver to allocate Resources (rooms, staff, equipment) across VisitStages.
  7. P0 The system shall maintain a ReminderLadder on each Appointment, triggering SMS/email/voice at configurable cadence.
  8. P0 The system shall support multi-channel booking (FrontDesk, Portal, Phone, WalkIn, WaitlistOffer) with channel recorded on each Appointment.
  9. P0 The system shall enforce all 12 constraint rules during slot selection and booking.
  10. P1 The system shall maintain a priority-scored Waitlist and auto-offer freed slots to the highest-scoring entry.
  11. P1 The system shall expire WaitlistEntry offers after OfferExpires and re-queue the entry.
  12. P1 The system shall track reschedule chains via RescheduledFromAppointmentID and warn at chain length > 3.
  13. P1 The system shall support group visits via VisitType.ScheduleKind = GroupVisit with shared slot capacity.
  14. P2 The system shall provide a provider utilization dashboard showing slot fill rate, no-show rate, and average wait time.

Non-Functional Requirements

icApplication Overrides

How the Scheduling noun-apps are generated by IC on top of Cosmos DB + a constraint-solver library, surfaced as FHIR Schedule / Slot / Appointment, and dispatched through a pre-visit reminder ladder. The scheduling-engine is one of the few rev.health modules that ships a domain-specific binding override (the resource-graph component) on top of the IC default component generation.

Shared generation map. The IC platform code-gen flow, default bindings, environment cascade, RBAC model, and CLI commands are identical across all modules. See the IC Reference for the canonical shared reference. This section covers only Scheduling-specific content.

Technology Stack P0

The shared platform stack (Cosmos DB, Redis, IC-generated API, Angular 21, SQS+SNS, OpenTelemetry, etc.) is documented in the IC Reference. Module-specific additions:

LayerChoiceNotes
Solver libraryGoogle OR-Tools CP-SAT (primary) / Timefold (fallback)Constraint-programming kernel; in-process Java/Python sidecar
Scheduling enginescheduling-engine internal libraryWraps OR-Tools with the rev.health resource-graph domain model
Reminder dispatcherInternal queue + Twilio (SMS) + SendGrid (email)TCPA opt-in tracked per channel
Realtime status boardServer-Sent Events over the API gateway2-second freshness target
Calendar exportiCal feed (read-only, signed)CalDAV / Google / Outlook two-way sync deferred to P2
Object storageS3 with Object LockAudit log + reminder-message archive

Project Directory Layout P0

This module uses a non-standard directory layout; the standard skeleton is documented in the IC Reference.

scheduling/
├── ami/
│   ├── Schedule.ami.json
│   ├── Slot.ami.json
│   ├── Appointment.ami.json
│   ├── Resource.ami.json
│   ├── VisitType.ami.json
│   ├── VisitStage.ami.json
│   └── WaitlistEntry.ami.json
├── container-definitions/   (generated by IC)
│   ├── 001_schedules.sql
│   ├── 002_slots.sql
│   ├── 003_appointments.sql
│   ├── 004_resources.sql
│   ├── 005_visit_types.sql
│   ├── 006_visit_stages.sql
│   ├── 007_waitlist_entries.sql
│   └── 099_rls_policies.sql
├── api/                              (generated by IC)
│   ├── schedules.controller.ts
│   ├── slots.controller.ts
│   ├── appointments.controller.ts
│   ├── resources.controller.ts
│   ├── visit-types.controller.ts
│   ├── visit-stages.controller.ts
│   └── waitlist.controller.ts
├── api-fhir/                         (FHIR facade; hand-written)
│   ├── schedule.fhir.mapper.ts
│   ├── slot.fhir.mapper.ts
│   └── appointment.fhir.mapper.ts
├── components/                       (generated + override)
│   ├── ic-ng21-schedule-1-app/
│   ├── ic-ng21-slot-1-app/
│   ├── ic-ng21-appointment-1-app/
│   ├── ic-ng21-resource-1-app/
│   ├── ic-ng21-visit-type-1-app/
│   ├── ic-ng21-waitlist-entry-1-app/
│   └── ic-ng21-scheduling-1-resource-graph/   (override binding)
├── lib/
│   └── scheduling-engine/            (OR-Tools wrapper + domain model)
│       ├── solver.ts
│       ├── resource-graph.ts
│       ├── stage-placement.ts
│       └── walk-in-insertion.ts
├── workers/
│   ├── reminder-ladder.worker.ts     (T-30 / T-14 / T-3 / T-day)
│   ├── no-show-detector.worker.ts    (T+10 sweeper)
│   └── waitlist-auto-fill.worker.ts  (5-min SMS offer)
├── env/
│   ├── dev.env
│   ├── qa.env
│   ├── uat.env
│   └── prod.env
└── tests/
    ├── solver.spec.ts
    ├── concurrency.spec.ts
    └── reminder-ladder.spec.ts

Scheduling code-gen details

The scheduling-engine library and FHIR facade are hand-written and consumed by the generated API layer.

Domain-Specific Override Bindings P1

One binding is overridden at the module level: the default ic-ng21-core-1-jtable for Slot is replaced by ic-ng21-scheduling-1-resource-graph. The default jtable presents Slots as a flat grid keyed on SlotStartDateTimeUTC; the resource-graph component renders Slots as paths through the resource graph and surfaces feasibility explanations inline.

Entity / SurfaceDefault BindingOverride BindingReason
Slot search / availability view ic-ng21-core-1-jtable ic-ng21-scheduling-1-resource-graph Flat grid cannot express multi-resource paths; the resource-graph view shows provider, room, MA, and equipment lanes simultaneously.
Live status board (no IC default) ic-ng21-scheduling-1-status-board SSE-driven board purpose-built for back-office wall display.
Reminder ladder editor ic-ng21-core-1-text-area on JSON state ic-ng21-scheduling-1-reminder-ladder Visual T-30/T-14/T-3/T-day cadence editor with TCPA opt-in checkbox per channel.
VisitType / VisitStage composer ic-ng21-core-1-jtable on VisitStage ic-ng21-scheduling-1-stage-composer Drag-to-reorder stages with named-resource picker per stage.

Pre-Visit Reminder Ladder Dispatcher P1

The reminder dispatcher is a scheduled worker (reminder-ladder.worker.ts) that fires four reminders per appointment by default. The cadence is configurable per VisitType, but the v1 default is T-30, T-14, T-3, and T-day.

RungTrigger timeDefault channelBody templateReply behavior
T-3030 days before SlotStartDateTimeUTCEmailConfirmation + portal self-reschedule linkNone (read receipts only)
T-1414 days beforeEmail + SMS (if opted-in)Reminder + intake-form prompt"C" confirms; "1" reschedules
T-33 days beforeSMS (TCPA opt-in required)Confirm or reschedule"C" confirms (Booked → Confirmed); "1" opens self-rebook; "STOP" opts out
T-dayDay-of, 8:00 localSMSReminder with location, parking, intake checklist"C" confirms; missed → counted toward no-show prediction

Worker behavior (pseudocode):

// Every minute, the worker scans the ReminderLadderState index for due rungs.
for each Appointment a in due_rungs(now):
  rung = a.next_due_rung()
  if !patient_opted_in(a.PatientID, rung.channel):
    skip_rung(a, rung, reason="opt-out")
    continue
  if a.AppointmentStatus in ["Cancelled", "NoShow", "Completed"]:
    cancel_remaining_rungs(a)
    continue
  body = render_template(rung.template, a)
  dispatcher.send(a.PatientID, rung.channel, body)
  audit_log.write(a, rung, "sent")
  a.ReminderLadderState = advance(a.ReminderLadderState, rung)
  persist(a)

The dispatcher runs on a 60-second tick. NFR-6 requires that 99.5% of messages dispatch within 5 minutes of their scheduled fire-time; the queue is sized to absorb 100K appointments-in-flight per tenant without breaching the SLA.

Versioning — Worked Example P0

Every versionable artifact in the Scheduling module follows IC SemVer per Hard Rule #9: break.feature.bug.buildtimestamp. The example below traces the lifecycle of the Slot AMI from its initial schema shape through a feature addition and a bug fix.

VersionChangeComponent tagNotes
1.0.0.20260301T0900ZInitial schema: Slot with 11 fieldsic-ng21-slot-1-appTag's break segment is 1
1.1.0.20260420T1145ZAdded OverbookAllowedBool (nullable, defaults false) — backward-compatible featureic-ng21-slot-1-appSame tag; new CDN URL; consumers auto-pick up via DI binding
1.1.1.20260512T0820ZBug fix: optimistic concurrency error message localizationic-ng21-slot-1-appSame tag; consumers auto-pick up
2.0.0.20260901T1400ZBreak: rename SlotStatusString50SlotStatusEnum with constrained domain; legacy String kept on 1.xic-ng21-slot-2-appNew tag (break segment 2); per IC Hard Rule #13, new database; ETL from 1.x to 2.x runs blue-green; 1.x stays live for rollback
Illustrative example. The version timestamps and the precise contents of each release above are illustrative — they describe how the SemVer contract would apply to the Scheduling module if a real change were made. They are not derived from a checked-in change log.

Scheduling environment details

KeyDevQAUATProd
scheduling.solver.timeoutMs500500250250
scheduling.solver.workerCount1248
reminder.dispatcher.dryRunBooltruetruefalsefalse
reminder.sms.providertwilio-testtwilio-testtwilio-prodtwilio-prod

Scheduling RBAC notes

ABAC overlays for patient-context apply (a patient sees only their own appointments).

OperationMin permission levelABAC predicate
Read own appointments10 (Patient)Appointment.PatientID = subject.PatientID
Search slots / book30 (Front-Desk)OrganizationID = current_org()
Update appointment status40 (MA/RN)OrganizationID = current_org()
Edit own schedule blocks50 (Provider)Schedule.ProviderID = subject.ProviderID
Edit any schedule / VisitType / VisitStage70 (Practice Manager)OrganizationID = current_org()
Override block schedules60OrganizationID = current_org()
Edit AMI / break-change90 (Org Admin / EditAMI)

Cross-Module Links

See also: Scheduling walkthrough · IC Reference