Appointments in ClinicFlow follow a strict lifecycle enforced at the domain level. Every status transition is gated by invariants on 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 aggregate. Commands are dispatched via MediatR and are grouped by the actor initiating the action — Patient, Doctor, or Staff (Receptionist/Admin). All write commands commit via IUnitOfWork and raise domain events consumed by registered INotificationHandler implementations.
The AppointmentStatus enum governs the full lifecycle:
| Value | Name | Description |
|---|---|---|
| 1 | Scheduled | Newly booked, awaiting check-in |
| 3 | InProgress | Doctor has started the consultation |
| 4 | Completed | Consultation finished |
| 5 | Cancelled | Cancelled within the allowed window — no penalty |
| 6 | NoShow | Patient did not attend |
| 7 | LateCancellation | Cancelled after the allowed window — penalty applied |
| 8 | CheckedIn | Patient arrived and checked in by staff |
| 9 | RequiresReassignment | Doctor was suspended; awaiting reassignment |
Scheduling Commands
- Patient
- Doctor
- Staff
ScheduleByPatientCommand
Schedules a new appointment initiated by a patient (or a guardian acting on behalf of a patient).Returns: IRequest<Guid> — the new appointment’s Id.The
UserId of the logged-in patient (or guardian) dispatching the command.The
Patient.Id for whom the appointment is being booked. May differ from the initiator if a guardian is acting on behalf of a dependent.The
Doctor.Id to book with.The
AppointmentTypeDefinition.Id defining the type of consultation.The appointment date. Must be today or in the future (validated against
TimeProvider.GetUtcNow()).Appointment start time.
Appointment end time. Must be after
StartTime.Optional patient-supplied notes. Maximum 500 characters.
- Resolves
TargetPatient,InitiatorPatient(viapatientRepository.GetByUserIdAsync),Doctor,User(forIsPhoneVerified), andAppointmentType. - Loads active patient penalties from
IPatientPenaltyRepository. - Fetches the doctor’s schedule for the day via
IScheduleRepository.GetByDoctorAndDayAsync. - Checks for slot conflicts via
IAppointmentRepository.HasConflictAsync. ThrowsAppointmentConflictExceptionon conflict. - Calls
IRegionalSchedulingService.EnforceSchedulingRegulationsfor regional compliance. - Delegates to
AppointmentSchedulingService.ScheduleByPatient(...)which validates phone verification, patient–guardian relationships, age eligibility, penalty eligibility, and slot fit. - Persists and returns the new
Appointment.Id.
AppointmentScheduledEventThe initiating patient’s phone must be verified (
IsPhoneVerified = true) before this command succeeds. Use SendPhoneVerificationCommand and VerifyPhoneCommand to complete verification first.Rescheduling Commands
- Patient
- Doctor
- Staff
RescheduleByPatientCommand
Reschedules an existing appointment at the patient’s request. Patients are limited to one reschedule per appointment (RescheduleCount >= 1 throws).Returns: IRequest (no return value)The
UserId of the patient initiating the reschedule.The appointment to reschedule.
New appointment date. Must be in the future.
New start time.
New end time. Must be after
NewStartTime.Updated patient notes. Maximum 500 characters.
AppointmentReschedulingService.RescheduleByPatient.Domain event raised: AppointmentRescheduledEvent (carries previousDate and previousTimeRange)Cancellation Commands
- Patient
- Doctor
- Staff
CancelAppointmentByPatientCommand
Cancels an appointment at the patient’s request. The handler applies regional late-cancellation rules; if cancelled too close to the appointment time, status becomes LateCancellation (penalty applied) rather than Cancelled.Returns: IRequest (no return value)The appointment to cancel.
The
UserId of the patient (or guardian) initiating the cancellation.Optional free-text cancellation reason stored on the appointment.
AppointmentCancellationService.CancelByPatient(...) with a context containing the specialty and category — used by the regional service to determine the late-cancellation window.Domain events raised: AppointmentCancelledEvent (normal) or AppointmentLateCancelledEvent (late). The AppointmentLateCancelledEventHandler processes penalty creation.Lifecycle Transition Commands
CheckInAppointmentByStaffCommand
Records a patient’s arrival and moves the appointment from Scheduled → CheckedIn.
Returns: IRequest (no return value)
The appointment to check in. Must be in
Scheduled status.Optional arrival notes stored on
Appointment.ReceptionistNotes.appointment.CheckIn(today, receptionistNotes). Throws DomainValidationException (DomainErrors.Appointment.CannotCheckIn) if not in Scheduled status. Records CheckedInAt date.
Domain event raised: AppointmentCheckedInEvent
StartAppointmentByDoctorCommand
Starts a consultation, transitioning the appointment from CheckedIn → InProgress. Only the appointment’s assigned doctor can start it.
Returns: IRequest (no return value)
The appointment to start. Must be in
CheckedIn status.The
UserId of the doctor. Resolved to Doctor; doctor.Id must match appointment.DoctorId, otherwise DomainValidationException (DomainErrors.Appointment.UnauthorizedDoctor) is thrown.AppointmentStartedEvent
No-Show Commands
MarkAppointmentAsNoShowByDoctorCommand
Marks a Scheduled appointment as a no-show, initiated by the treating doctor. Only the assigned doctor can use this variant.
Returns: IRequest (no return value)
The appointment. Must be in
Scheduled status.The
UserId of the doctor. doctor.Id must match appointment.DoctorId; otherwise AppointmentNoShowUnauthorizedException is thrown.AppointmentMarkedAsNoShowEvent. The AppointmentMarkedAsNoShowEventHandler creates a patient penalty record.
MarkAppointmentAsNoShowByStaffCommand
Marks a Scheduled appointment as a no-show, initiated by staff. No doctor-ownership check is performed.
Returns: IRequest (no return value)
The appointment. Must be in
Scheduled status.AppointmentMarkedAsNoShowEvent
ReassignAppointmentCommand
Reassigns aRequiresReassignment appointment to a new doctor with a new slot. Used after a doctor suspension.
Returns: IRequest (no return value)
The appointment in
RequiresReassignment status.The
Doctor.Id to assign the appointment to.New appointment date.
New start time.
New end time. Must be after
NewStartTime.AppointmentReassignmentService.Reassign(...) which transitions status back to Scheduled with the new doctor and slot.
Domain event raised: AppointmentReassignedEvent (carries previousDoctorId)
CleanExpiredDisplacedAppointmentsCommand
Background/scheduled command that cancels anyRequiresReassignment appointments whose ScheduledDate has already passed (system timeout). No patient penalty is applied as the clinic bears responsibility.
Returns: IRequest (no return value)
IHostedService).
Handler behaviour:
- Gets all expired displaced appointments via
IAppointmentRepository.GetExpiredDisplacedAppointmentsAsync(now). - Calls
appointment.CancelDueToSystemTimeout(today)on each — setsCancellationReason = "System timeout: Displaced appointment was not reassigned."and status toCancelled. - Persists all changes in one
SaveChangesAsynccall.
AppointmentSystemCancelledEvent
Notes Update Commands
UpdatePatientNotesByPatientCommand
Updates the patient-visible notes on an appointment, initiated by the patient (or guardian).
Returns: IRequest (no return value)
The appointment to update. Must be in
Scheduled or RequiresReassignment status.The
UserId of the patient initiating the update. PatientAccessService.EnsureCanActOnBehalfOf validates that the initiator is the patient or their guardian.New notes.
null clears the field.UpdatePatientNotesByStaffCommand
Updates the patient notes on an appointment, initiated by staff. No ownership check; staff can update any patient’s notes.
Returns: IRequest (no return value)
The appointment. Must be in
Scheduled or RequiresReassignment status.New patient notes.
null clears the field.UpdateReceptionistNotesByStaffCommand
Updates the internal receptionist notes on an appointment. Only allowed when the appointment is in CheckedIn status.
Returns: IRequest (no return value)
The appointment. Must be in
CheckedIn status. Throws DomainValidationException (DomainErrors.Appointment.CannotUpdateNotes) otherwise.New receptionist notes.
null clears the field.Queries
GetAppointmentByIdQuery
Retrieves a single appointment by its Id.
Returns: IRequest<AppointmentDto>
The appointment’s primary key. Throws
EntityNotFoundException if not found.GetAppointmentsByPatientIdQuery
Retrieves a paginated list of all appointments for a specific patient.
Returns: IRequest<PaginatedList<AppointmentDto>>
The
Patient.Id (not the UserId) to filter by.1-based page index.
Records per page.
GetAppointmentsByDoctorIdQuery
Retrieves a paginated list of a doctor’s appointments filtered to a specific date.
Returns: IRequest<PaginatedList<AppointmentDto>>
The
Doctor.Id to filter by.The specific date to retrieve appointments for.
1-based page index.
Records per page.
GetAppointmentsByDateRangeQuery
Retrieves a paginated list of all appointments within an inclusive date range across all doctors and patients.
Returns: IRequest<PaginatedList<AppointmentDto>>
Inclusive range start date.
Inclusive range end date.
1-based page index.
Records per page.
AppointmentDto Response Shape
All appointment queries returnAppointmentDto or PaginatedList<AppointmentDto>.
The appointment’s primary key.
The
Patient entity’s Id (not the UserId).The
Doctor entity’s Id.The
AppointmentTypeDefinition.Id.The date the appointment is scheduled for.
Start time of the appointment slot (from
TimeRange.Start).End time of the appointment slot (from
TimeRange.End).Current lifecycle status. See the status table at the top of this page.
Patient-supplied notes. Empty string if none.
Staff/receptionist notes added at check-in. Empty string if none.