Appointments in ClinicFlow follow a strict, domain-enforced lifecycle. Every state transition is guarded by business rules inside theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/0Crazy-0/ClinicFlow/llms.txt
Use this file to discover all available pages before exploring further.
Appointment entity and its supporting domain services, ensuring that no appointment can skip a step, be cancelled when already cancelled, or be started by the wrong doctor. Understanding these transitions is essential for building any integration or front-end workflow on top of the platform.
Appointment Statuses
TheAppointmentStatus enum defines every valid state an appointment can occupy:
| Value | Integer | Meaning |
|---|---|---|
Scheduled | 1 | Created and waiting for the patient to arrive |
CheckedIn | 8 | Patient has arrived at the clinic |
InProgress | 3 | Doctor has started the consultation |
Completed | 4 | Consultation finished successfully |
Cancelled | 5 | Cancelled within the allowed window — no penalty |
LateCancellation | 7 | Cancelled after the window — penalty applied |
NoShow | 6 | Patient did not arrive |
RequiresReassignment | 9 | Original doctor suspended; awaiting a new doctor |
Normal Appointment Flow
Schedule
A patient, doctor, or staff member creates the appointment. The appointment enters the
Scheduled state and the AppointmentScheduledEvent domain event fires. RescheduleCount is initialised to 0.Check In
A staff member calls
CheckInAppointmentByStaff. The appointment moves to CheckedIn. Optional receptionist notes may be added at this point and stored in ReceptionistNotes. CheckedInAt is recorded.Start
The assigned doctor calls
StartAppointmentByDoctor. The domain enforces that the initiating doctor’s Id matches appointment.DoctorId — no other doctor can start the consultation. Status becomes InProgress.Scheduling Actors
Three actor roles can create appointments, each with different constraints enforced byAppointmentSchedulingService.
Patient
Uses
ScheduleByPatient. The initiator’s phone must be verified, the target patient’s profile must be complete (blood type + emergency contact), and active penalties must not block the requested date. Overbooking is not permitted — the requested time range must fall within the doctor’s active schedule slot for that day.Doctor
Uses
ScheduleByDoctor. The scheduling doctor must be the same doctor as InitiatorDoctor. Doctors can set IsOverbook = true to bypass slot availability and conflict checks. Guardian consent must be verified explicitly rather than derived from the patient’s relationship.Staff
Uses
ScheduleByStaff. Similar to doctor scheduling but the target doctor is specified by DoctorId. Staff can also overbook (IsOverbook = true). The target patient’s profile must be complete. No penalty checks are run for staff-initiated scheduling.All actors
All scheduling paths go through the same
Appointment.Schedule(...) factory. All require a valid SchedulingClearance issued by the regional scheduling service, and all validate patient age eligibility against the appointment type’s policy.ScheduleByPatientCommand
The patient-facing scheduling command is the most constrained of the three:InitiatorUserId identifies the user making the request; TargetPatientId identifies the patient being booked (these differ when a parent books for a child). The handler resolves both patient profiles, verifies their relationship via PatientAccessService.EnsureCanActOnBehalfOf, and then delegates to AppointmentSchedulingService.ScheduleByPatient.
ScheduleByDoctorCommand
GetByUserIdAsync(InitiatorUserId). Setting IsOverbook = true bypasses the slot-availability check entirely.
ScheduleByStaffCommand
DoctorId rather than derived from the initiator.
Patient Notes vs Receptionist Notes
Appointments carry two separate free-text fields for contextual information.| Field | Updated by | Status constraint |
|---|---|---|
PatientNotes | Patient (via UpdatePatientNotesByPatient) or Staff (via UpdatePatientNotesByStaff) | Only when status is Scheduled or RequiresReassignment |
ReceptionistNotes | Staff only (via UpdateReceptionistNotesByStaff) | Only when status is CheckedIn |
PatientNotes captures pre-visit information supplied by the patient or entered by a receptionist on their behalf. ReceptionistNotes captures observations made at check-in and is locked to the CheckedIn state to prevent post-visit modifications.
Check In
CheckInAppointmentByStaff is the only command that moves an appointment from Scheduled to CheckedIn:
Scheduled status. Attempting to check in an appointment that is CheckedIn, InProgress, or in any terminal state throws a domain validation exception.
No-Show
When a patient fails to appear for aScheduled appointment, staff or the assigned doctor can mark it as NoShow.
MarkAsNoShowByStaff— any staff member can call this; no actor identity check beyond the role.MarkAsNoShowByDoctor— the system verifies that the initiating doctor’sIdmatchesappointment.DoctorId. A doctor who is not the appointment owner receives anAppointmentNoShowUnauthorizedException.
MarkAsNoShow() and raise AppointmentMarkedAsNoShowEvent. The appointment must be in Scheduled status for both paths.
Cancellation
ClinicFlow distinguishes three cancellation paths, each producing a different domain event and penalty outcome.Normal Cancellation — Cancel
Triggered when the cancellation is within the specialty’s allowed window. Status becomes Cancelled. AppointmentCancelledEvent fires. No patient penalty is applied.
AppointmentCancellationService.CancelByPatient, which checks the specialty’s IsCancellationAllowed rule at runtime. If the window has not passed, it calls appointment.Cancel(...); otherwise it automatically calls appointment.CancelLate(...).
Late Cancellation — CancelLate
When the patient cancels after the specialty-defined cancellation window, CancelLate is invoked automatically. Status becomes LateCancellation and AppointmentLateCancelledEvent fires, which downstream services can use to issue a penalty.
Procedure-type appointments cannot be cancelled by patients. Any attempt throws
AppointmentCancellationUnauthorizedException. Emergency appointments can only be cancelled by the patient themselves, or by a parent if the patient is a child under 18.System Timeout Cancellation — CancelDueToSystemTimeout
System timeout cancellations carry no patient penalty. The appointment entered
RequiresReassignment because of a clinic-side event (doctor suspension), and it is the clinic’s responsibility to reassign it in time. If the scheduled date passes without reassignment, CleanExpiredDisplacedAppointments cancels the appointment and fires AppointmentSystemCancelledEvent. The CancellationReason is set to the constant "System timeout: Displaced appointment was not reassigned." and CancelledByUserId is null.CancelDueToSystemTimeout can only be called on appointments in RequiresReassignment status. Calling it on any other status throws a domain validation exception.
Rescheduling
Any actor (patient, doctor, or staff) can reschedule aScheduled appointment by providing a new date and time range. AppointmentReschedulingService enforces the same availability and clearance rules as scheduling.
Patient rescheduling also re-validates penalty history against the new date, and the patient’s phone must remain verified. Optional NewPatientNotes may be supplied to update PatientNotes in the same transaction.
RequiresReassignment & Doctor Reassignment
When a doctor is suspended, all their futureScheduled appointments are marked RequiresReassignment via appointment.MarkAsRequiresReassignment(). The appointment is placed in a holding queue — the patient is not penalised and the appointment remains active.
Staff must resolve each displaced appointment using ReassignAppointment:
AppointmentReassignmentService.Reassign validates that the new doctor’s schedule covers the requested time range, then calls appointment.Reassign(...) which resets DoctorId, ScheduledDate, and TimeRange and sets the status back to Scheduled. AppointmentReassignedEvent fires to notify the patient.
CleanExpiredDisplacedAppointments
CleanExpiredDisplacedAppointmentsCommand is a parameterless command intended to be dispatched by a background job (e.g., a Hangfire recurring task or an IHostedService timer).
IAppointmentRepository.GetExpiredDisplacedAppointmentsAsync(now), which returns all appointments in RequiresReassignment status whose ScheduledDate and TimeRange.Start have already passed. Each is cancelled via CancelDueToSystemTimeout in a single SaveChangesAsync call.
AppointmentDto
Queries that return appointment data useAppointmentDto:
CancelledAt, CancellationReason, CancelledByUserId) and CheckedInAt are intentionally omitted from the DTO — they are available by fetching the full entity through staff-facing endpoints.
Command Reference
| Command | Actor | Effect |
|---|---|---|
ScheduleByPatientCommand | Patient | Creates appointment in Scheduled |
ScheduleByDoctorCommand | Doctor | Creates appointment in Scheduled; can overbook |
ScheduleByStaffCommand | Staff | Creates appointment in Scheduled; can overbook |
CheckInAppointmentByStaffCommand | Staff | Scheduled → CheckedIn |
StartAppointmentByDoctorCommand | Assigned doctor | CheckedIn → InProgress |
CompleteAppointmentCommand | Doctor | InProgress → Completed |
MarkAppointmentAsNoShowByStaffCommand | Staff | Scheduled → NoShow |
MarkAppointmentAsNoShowByDoctorCommand | Assigned doctor | Scheduled → NoShow |
CancelAppointmentByPatientCommand | Patient | Scheduled → Cancelled or LateCancellation |
CancelAppointmentByDoctorCommand | Assigned doctor | Scheduled → Cancelled |
CancelAppointmentByStaffCommand | Staff | Scheduled → Cancelled (reason required) |
RescheduleByPatientCommand | Patient | Updates date/time (max 1 reschedule) |
RescheduleByDoctorCommand | Assigned doctor | Updates date/time; can overbook |
RescheduleByStaffCommand | Staff | Updates date/time; can overbook |
ReassignAppointmentCommand | Staff | RequiresReassignment → Scheduled with new doctor |
CleanExpiredDisplacedAppointmentsCommand | System/Background | RequiresReassignment → Cancelled (no penalty) |
UpdatePatientNotesByPatientCommand | Patient | Updates PatientNotes |
UpdatePatientNotesByStaffCommand | Staff | Updates PatientNotes |
UpdateReceptionistNotesByStaffCommand | Staff | Updates ReceptionistNotes (only when CheckedIn) |