# Missed Appointment Notifications ## What it does When an appointment is cancelled or marked as a no-show, this plugin automatically creates a follow-up **task to reschedule the patient**, so a missed appointment never quietly falls through the cracks. The task is routed to a scheduling team (if one is configured) or to the appointment's provider, and carries a short summary of the original appointment. ## Problem it solves When a patient cancels or doesn't show up, someone has to remember to call them back and rebook — today that depends on a staff member noticing and following up manually. These appointments get missed, patients go un-booked, and care gaps open up. This plugin turns every cancellation and no-show into an explicit, assigned task so the rebooking work is always captured and visible. ## Who it's for Scheduling coordinators and front-desk staff who own rebooking, and the providers whose missed visits need to be filled. It's specialty-agnostic — any practice that schedules appointments in Canvas. ## How it works The handler responds to the `APPOINTMENT_CANCELED` and `APPOINTMENT_NO_SHOWED` events and creates: - a **task** titled with the provider and the original appointment date/time (noting whether the appointment was *cancelled* or *no-showed*), linked to the patient, due a configurable number of days later (see `RESCHEDULE_DUE_DAYS`; defaults to the next day); and - a **comment** summarising the original appointment: reason for visit, provider, date/time, location, and note type. Routing: a **scheduling team** (see `SCHEDULING_TEAM_NAME`) if one matches, otherwise the **appointment's provider**. **Labels:** the task inherits the original appointment's active labels, and adds a `Reschedule` label only if a label of that name already exists in the instance (it never creates new labels). **Reason for visit** is read from the appointment note's *Reason For Visit* command (structured coding and free-text comment combined when both exist), falling back to the appointment's comment, then `Not documented`. **Times** are rendered in the instance's configured timezone (`self.environment["INSTALLATION_TIME_ZONE"]`), falling back to UTC. The handler does nothing when the appointment can't be found, is marked entered-in-error, or its start time is missing. For **cancellations** it also skips appointments whose start time has already passed (there's nothing left to reschedule); **no-shows** are always in the past by definition, so that guard doesn't apply to them. ## How to install ``` canvas install missed-appointment-notifications ``` Then (optionally) set the `SCHEDULING_TEAM_NAME` variable on the plugin's configuration page: `/admin/plugin_io/plugin//change/`. ## Configuration options Configured via plugin **variables** (declared in `CANVAS_MANIFEST.json`): | Variable | Required | Description | |---|---|---| | `SCHEDULING_TEAM_NAME` | optional | Exact name of the Team that reschedule tasks should be assigned to (matched case-insensitively, e.g. `Scheduling`). If unset/blank or no team matches, tasks are assigned to the appointment's provider. | | `RESCHEDULE_DUE_DAYS` | optional | Whole number of days (1–366) after the cancellation/no-show that the reschedule task is due. Defaults to `1` when unset. A blank, non-numeric, or out-of-range value is ignored (with a logged warning) and falls back to the default. | No code changes are needed to customise routing or timing — leave `SCHEDULING_TEAM_NAME` blank to always assign to the provider, and set `RESCHEDULE_DUE_DAYS` (e.g. `7`) to change how long staff have before the task is due. The display timezone is taken from the instance configuration (`INSTALLATION_TIME_ZONE`), not a setting. ## Screenshots The reschedule task created in a patient's chart after a cancellation — titled with the provider and original appointment time, due after the configured number of days, labelled `Reschedule`, with a comment summarising the original appointment: ![Reschedule task generated from a cancelled appointment](docs/reschedule-task.png) ## Development ``` uv sync uv run pytest # run tests uv run mypy missed_appointment_notifications ``` ## License MIT — see [LICENSE](../LICENSE).