# EMR21 — User Manual

> **Audience:** Clinical and administrative end users of EMR21 (physicians, nurses, secretaries, administrators, residents and clinical clerks, and patients using the portal).
> **Last updated:** 2026-11-01
> **Maintained automatically:** This manual is refreshed on a recurring schedule (every 2–3 days) to track ongoing UI/UX changes. If a screen looks different from what is described here, the underlying feature may have moved; the navigation labels are the source of truth.

---

## 1. What EMR21 Is

EMR21 is a physician-designed electronic medical record that runs the whole clinic from one screen. A clinician moves through the schedule, the inbox, a patient's chart, the visit note, prescriptions, labs, recalls, and billing without leaving the application. It fits any Ontario outpatient or community clinic rather than a single specialty, and it is built to be fast, low-click, and defensible under PHIPA from the first sign-in.

One deployment runs two separate applications. Clinic staff (physicians, nurses, secretaries, admins, and residents or clinical clerks on placement) sign in at `/login`. Patients use their own portal at `/portal/login`. Patients never see the clinic side, and clinic staff never sign in through the portal.

---

## 2. User Types and What Each Can Do

Access is controlled by **role-based access control (RBAC)**. Your role is attached to your login and determines which menu items appear and which actions you can take. Clinical reads and writes are always written to an audit log regardless of role.

| Role | Intended user | Core capabilities |
|------|---------------|-------------------|
| **Physician** (`physician`) | Family physician / NP | Full chart access, write and sign clinical notes, manage the clinical inbox, prescribe, order and acknowledge labs, manage recalls and panel, bill, send correspondence and messages, edit own settings |
| **Nurse** (`nurse`) | Clinical support staff | Schedule, patient demographics and clinical data entry (vitals, immunizations), Staff Inbox, recalls, replies to portal patients; starts and closes encounters but does not write notes in them or bill them |
| **Secretary** (`secretary`) | Front desk / admin staff | Schedule, demographics, booking, Staff Inbox, sending recalls and keeping a personal task list, correspondence handling, read-only lab results, the Panel's counts and practice summary; no clinical note authorship, and what they see of notes is set by the practice (§5.15) |
| **Admin** (`admin`) | Practice administrator | Everything above **plus** user management (create/disable accounts, assign roles) via the Admin panel, and practice settings |
| **Resident** (`resident`) | Postgraduate trainee | Opens charts and reads labs, prescriptions, and referrals; starts encounters and writes notes under a preceptor. A resident's signature sends the note to the preceptor for review (see §5.7) |
| **Clinical clerk** (`student`) | Medical student on placement | The same access as a resident, labelled clinical clerk in the app |
| **Portal patient** (`portal_patient`) | Patient | Separate portal only — view own health summary, book appointments, complete intake forms, send requests/messages to the practice |

> Staff-type roles (nurse, secretary, staff, admin) see a **Staff Inbox** tab tailored to non-physician triage work. The physician **Inbox** is the clinical results/correspondence inbox.

---

## 3. Logging In

1. Go to the clinic app URL (in local demo: `http://localhost:5173`).
2. Enter your email and password and select **Sign in**.
3. You land on the **Dashboard**.

![Screenshot — EMR21 login screen.](images/screen_1_login.png)

*Screenshot — the clinic sign-in page. (All screenshots in this manual are of the live app with synthetic demo data — no real PHI.)*

**Demo accounts** (synthetic data only):

| Role | Email | Password |
|------|-------|----------|
| Physician | `dr.garg@emr21.local` | `Doctor1234!` |
| Admin | `admin@emr21.local` | `Admin1234!` |
| Staff | `staff@emr21.local` | `Staff1234!` |
| Nurse | `nurse@emr21.local` | `Nurse1234!` |
| Secretary | `secretary@emr21.local` | `Secretary1234!` |
| Resident | `resident@emr21.local` | `Resident1234!` |
| Clinical clerk | `student@emr21.local` | `Student1234!` |

Security notes that affect your login experience:

- Passwords must contain at least 8 characters with upper- and lower-case letters, a digit, and a special character.
- After 5 failed attempts within a 15-minute window the account is temporarily locked. Wait for the window to pass or ask an admin.
- Your sign-in token lasts 60 minutes and renews quietly while you work. The session behind it is held on the server and tied to the browser it was issued to, so a copied refresh cookie will not renew from another machine, and a session can be ended from the server side. Sessions last up to 7 days.
- If a patient signs in to the portal on a browser where a clinician is signed in (a shared tablet in the waiting room, say), the clinician's session on that browser ends and can no longer renew, so the clinician has to sign in again.
- A session left idle is ended. The limit is the practice's Session timeout in Settings, Security & Privacy (60 minutes unless an admin changes it, anywhere from 5 to 480). When it runs out you are sent back to sign in with "Signed out after a period of inactivity — please sign in again".
- Use **Log out** (bottom of the left nav) when you leave the workstation.

**A new installation.** A freshly installed EMR21 has no accounts, so the sign-in page sends the first visitor to `/setup`. That page creates the practice (its name and, optionally, the 6-digit OHIP group billing number), the first administrator, and the AI setting for the machine: No AI on this appliance, or Local AI (on this machine). Press **Create practice and administrator**, then **Go to sign in**. Other staff accounts are made afterwards from the Admin panel. Once any account exists, `/setup` sends you to the sign-in page instead.

---

## 4. Getting Around

The clinic app has a left navigation rail, which collapses to a bottom tab bar on small screens, and a header along the top with a global patient search, today's date, a **Help** link, and (in demo builds) the Guided tour launcher. The search box jumps straight to any patient's chart from any page; it ignores accents, so typing Cecile finds a patient recorded as Cécile. It also ignores apostrophes, hyphens, and spaces, so obrien finds O'Brien and smithjones finds Smith-Jones. A search that matches nobody says so under the box, and a search that fails to reach the server says that instead. **Help** opens this manual inside the app at any time. Press ⌘K or Ctrl+K anywhere to open SKIPPY, the built-in AI assistant (see §5.16). On a wide screen SKIPPY opens as a panel on the right that narrows the page instead of covering it. On a phone it is a floating button that opens a bottom sheet.

![EMR21 navigation map — the clinic application and the patient portal, the role-aware left-nav items, and the SKIPPY launcher.](images/01_nav_map.svg)

*Figure 1 — The two applications and the clinic navigation rail.*

Navigation items (visibility depends on your role):

- **Dashboard** — your start-of-day overview.
- **Inbox** — clinical results and correspondence, AI-triaged by urgency (physician-facing).
- **Staff Inbox** — non-clinical triage queue (staff roles).
- **Messages** — internal messaging / patient messages, with an unread badge that polls every minute.
- **Patients** — searchable patient list; the gateway to charts.
- **Schedule** — today's clinic and future bookings.
- **Reminders** — overdue and upcoming preventive-care recalls and tasks (the page is titled *Reminders & Recalls*).
- **Panel** — roster / population-health view. Every clinic role sees the item. Physicians, nurses, and admins get the full page; a front-desk login gets the counts and the practice summary, and the page says that the patient-naming lists are not shown for their role (see §5.11).
- **Orders** — the outstanding-orders worklist: referrals, bloodwork, and imaging that have been sent and are waiting on a result or reply. Clinicians place new orders here and mark results in as they come back.
- **Billing** — OHIP billing for encounters.
- **Import** — bring in patient charts exported from another EMR (admin only).
- **Settings** — your profile and password (physician/admin).
- **Admin** — user management (admin only).

The **EMR21 wordmark** (top of the nav rail, or the mobile top bar) is a shortcut back to the Dashboard. The clinic name shown under it comes from the practice profile, so it always matches what an admin set in practice settings.

On a desktop the left menu can be narrowed to a strip of icons. Use the small panel button beside the wordmark, or press the `[` key when you are not typing in a field. Hover an icon to see its label; Messages shows a blue dot when something is unread, and Settings and Log out sit at the bottom of the strip. Every page is still one click away. The choice is saved to your account, so it follows you to any workstation you sign in at. An admin can switch this off for the practice (the Personal layout flag, §5.15), and the menu then stays full width.

If a page fails to draw, the menu and header stay where they are and the page area shows "This page ran into a problem" with **Try again** and **Go to dashboard**. Anything you saved or signed is already on the server. Moving to another page clears the error. If the whole app fails, a single panel offers **Reload**.

An address that matches no page shows a short "Page not found" message inside the app, naming the address and offering a link back to the Dashboard. Previously a stale bookmark landed you on the Dashboard with no explanation. Addresses for pages that were renamed still work: `/reminders` opens Reminders and `/orders` opens the Orders worklist. Inside a chart, a link that names a tab by a common alias lands on that tab rather than defaulting to Summary, and it keeps whatever it was pointing at highlighted. So `/chart/rx` and `/chart/meds` open Rx, `/chart/cpp` opens Summary, `/chart/results` opens Labs, `/chart/letters` opens Letters, `/chart/notes` opens Encounters, and `/chart/vaccines` opens Immunizations. A bare patient address with no tab (`/patients/<id>`) opens that chart's Summary tab, keeping any query string, instead of showing Page not found.

An address your role cannot open behaves the same way. A nurse or secretary who follows a link to Admin or Settings gets a "Not available for your role" page naming the address, the role it is restricted to, and the role they are signed in as. The same card appears inside a chart when a tab is restricted rather than empty, so a secretary opening the Rx tab reads that prescriptions are limited to physicians, nurses, staff, and admins instead of an empty list that looks like a patient with no medications. The API enforces the same rule on its own side, so this is only about being told what happened.

**Guided tour (demo builds).** In demo and dev builds, a Guided tour launcher runs spotlight walkthroughs by topic: App overview, Encounters, AI Scribe, Inbox workflow, and Forms. Each step opens the real page, so you see the live app rather than screenshots. In-chart guides use whichever patient you have open, or a seeded, AI-consented Demo Patient. Front-office logins (secretary, staff) are not offered the Encounters or AI Scribe guides, since those walk to controls their role cannot use. An admin can switch the launcher off for the whole practice from the Feature flags section of Settings (§5.15), with no deploy needed; the pill disappears for everyone at once.

---

## 5. The Clinic Day, Module by Module

![A clinic day in EMR21 — schedule, arrived, open chart, start encounter, document, sign, end & bill, then on to the next patient.](images/02_clinic_day.svg)

*Figure 2 — The shape of a clinic day, end to end.*

### 5.1 Dashboard
Your landing page and morning starting point. It shows a snapshot of the day's schedule, what needs attention in the inbox, and recalls that are due. From here you jump into the schedule or a chart.

For a physician, the overdue-recalls count covers only their own recalls, so two physicians looking at their dashboards on the same morning see different numbers. Nurses, secretaries, staff, and admins own no recalls of their own. They work the practice's list, so their tile counts every overdue recall in the practice and matches what the Reminders page shows. Before this fix their tile always read 0.

An **Overdue orders** tile counts bloodwork, imaging, and referrals that left the clinic and are still waiting past the practice's limit (14 days for bloodwork and 30 for imaging and referrals unless the practice has changed them). It turns amber when the count is above zero, and a click opens the Orders page on its Overdue list (§5.17). The tile is hidden when the practice has overdue tracking switched off.

A physician who supervises learners gets a Learner supervision card. It lists preceptor requests from residents and clinical clerks, each with **Confirm** and **Decline**, and the learner notes waiting for review, each marked Pending review; the heading counts how many are waiting. Click a note row to open the chart on that note. An admin sees every request and note in the practice, and each request names the physician it was sent to. The card only appears when there is something on it, and it is hidden when the Learner supervision flag is off (§5.15). For a resident or clerk, the Dashboard leaves out the billing and orders figures their role cannot read.

A badge in the top right reports whether the local AI model can answer right now. It reads "Checking AI services" while the app asks, "AI services warming up" when the model is installed but still loading (a cold load takes about 30 seconds), "AI services active" once the model has answered a test request, and "AI services offline" when the model cannot be reached. Offline or warming only means the AI shortcuts are unavailable for the moment; the EMR works manually either way.

### 5.2 Schedule
Today's clinic with each appointment's status moving through the visit lifecycle: **Scheduled → Arrived → In-Progress → Finished** (and Cancelled). Front-desk staff mark patients **Arrived**; a physician or nurse presses **Start Encounter**, and the physician works the in-progress list. A secretary sees "Arrived — the clinician starts the encounter" in place of the Start button, because starting an appointment opens a clinical encounter and the front desk cannot close one. Select an appointment to open the patient's chart in the context of that encounter. Bookings can also originate from the patient portal and appear here automatically. The booking window closes on Escape as well as on a click outside it, so the keyboard has a way out of it; it stays put while a booking is being saved. The Date field in the booking window is greyed out because a booking always goes on the day you're viewing, and a line under the fields says so. To book another day, move the schedule to that day first. Once you pick a patient in the booking window, the selected-patient chip shows their date of birth and age, so two patients with the same name are told apart before the booking is saved.

If the provider already has a live appointment overlapping the slot you are booking, an amber notice appears as soon as you choose the time: "Already booked — this provider has an appointment in this slot. Book anyway?" It used to show only after you pressed Book. The booking window names the appointment and offers **Book anyway** or **Pick another time**. Overbooking stays allowed; the advisory only makes sure it is deliberate. Booking the same patient with the same provider at the same start time twice is refused ("This patient is already booked with this provider at that time."), and a double click on the save button books once.

When you pick a patient and no provider is chosen yet, the provider field fills in with the patient's primary physician. You can change it. The booking window reads the practice's clinic hours for that weekday from practice settings. Under the time it says "Within office hours", or that the visit ends after the clinic closes, or that it runs past midnight. A booking that starts outside clinic hours, falls on a day the clinic is closed, or runs past midnight asks before it is saved ("This is outside practice hours (09:00–17:00). Book it anyway?"). Press **Book anyway** to keep it. Nothing outside hours is refused; the question just makes sure it was meant. If the slot also overlaps another appointment, the double-booking question comes after this one. Start times sit on the 15-minute grid (:00, :15, :30, :45).

The schedule's day runs on Ontario time. An appointment at 8 p.m. used to land on the next day's list, because the day boundary was taken in UTC.

Cancelling an appointment asks first. The dialog names the patient and the time, says the slot can be rebooked, and puts focus on Keep, so a stray click doesn't cancel anyone. Escape backs out of it. An appointment that is In Progress but whose encounter has since been cancelled offers Cancel as well as Complete, with a line saying the visit's encounter is no longer open.

Whose day you see: a physician opens the Schedule to their own day by default. Front desk, nursing, and admin see the practice-wide day, every provider's appointments, each card labeled with its provider, with a provider filter to focus one clinician's day so overlapping providers don't read as double-booking. For a physician the filter's default option reads My schedule, since the unfiltered day is already scoped to their own appointments. The filter lists every provider in the practice including whoever is signed in; it used to leave the signed-in physician out of its own list. On a phone the day collapses to a single list and each appointment carries the provider's name underneath the time, the same way the desktop columns carry it. A day with only one provider in it stays uncluttered on both.

When several appointments overlap at the same hour, the desktop grid splits them into columns. Once the columns get narrow enough that the status pill would squeeze the patient's name down to a couple of characters, the pill stands down and the status is carried by the coloured dot on the card. Hovering the card, or reading it with a screen reader, still gives the status in words.

Pressing **Start** on an appointment doesn't just flip its status — it **opens (or reuses) today's encounter** for that patient, so there is always a visit container to document and bill against (see §5.7). AI slot suggestions propose openings in clinic-local (Ontario) time. The slots themselves come from ordinary scheduling logic and are always offered. The short sentence explaining why a slot fits is written by the model, so it is gated by that patient's AI consent; on a chart that has declined AI you get the same slots with a templated explanation instead. The panel tells you which one you are reading. When the model wrote the sentence the footer says "AI suggestion — review before booking"; when it did not, either because consent was declined or because the model did not answer in time, it says the openings came from the schedule with no AI used.

### 5.3 Inbox (physician)
Incoming clinical items — lab results, imaging, consult letters, pharmacy queries, portal requests — each tagged with an **AI urgency triage** (e.g., a critical INR or an elevated HbA1c is surfaced to the top). Work top-down: open an item, review, and act (acknowledge, file to the chart, reply, or create a follow-up/recall). Inbox items are also visible inside the relevant patient's chart **Inbox** tab. The page is titled Physician Inbox for a physician and My Inbox for every other role.

An item can be handed to someone else. The **Assign to a clinician** picker in the item's detail lists the practice's physicians and nurses; pick one and the item leaves your inbox with a confirmation line. The server checks that the person you chose is an active user in your practice.

A completed new-patient intake arrives as a review item that carries the patient's answers, labelled and read-only, under the heading "NOT in the chart yet — confirm manually". Nothing from the intake is written to the chart until you enter it. If the patient has no primary physician, the item goes to whoever sent the intake link, flagged "no physician assigned". These items are labelled Intake, and the category filter at the top of the list has Intake and Document entries, so an intake or an uploaded document no longer shows up as "Other".

Closing a critical item with **No F/U** takes two steps. The first press of the send button opens a red confirmation inside the triage panel, "This result is flagged critical — close it with no follow-up?", with an optional reason box (for example, a repeat already came back normal). The send button then reads **Confirm: close critical result with no follow-up**. The reason is kept on the item as its triage note, and the audit log records that one was given. **Go back** cancels. The server refuses a critical No F/U without that confirmation too.

The patient chip on a row in the list opens the same chart tab as the link in the detail pane: Labs for a lab result, Rx for a refill request, the chart's last tab for anything else. The two links used to land on different tabs for the same item.

A critical result can reach more than the patient's own physician. If the practice has set up an on-call pool, each clinician in it gets the critical item as well. A result that sits unreviewed for a set number of days (3 unless the practice changes it, anywhere from 1 to 30) can be passed to the next clinician in that physician's coverage chain, and the forwarded item says how long it went unreviewed. Every hop is audit-logged. The pass happens when a physician or admin runs the escalation sweep, which is API-only for now; nothing runs it on a timer yet. The pool and the chains are set through the practice settings API by an admin (physicians and nurses can read them). There is no screen for any of this yet, and the Result escalation flag (§5.15) switches the whole thing off.

**AI draft reply** writes a reply for you to edit. The sender's message is passed to the model as quoted data, so an instruction hidden inside the message is never followed. The draft is screened before it reaches the editor: if it obeyed something the message told it to do, it is withheld and you are asked to write the reply by hand; if it claims something was already done, a warning says so. Bracketed placeholders such as "Dr. [Physician's Last Name]" are scrubbed out. If the draft promises something in your name ("I will call you Monday"), a note reads "This draft promises something on your behalf" and quotes the phrase, so you keep it only if you will do it. On a patient-linked item the draft needs that chart's AI consent.

A lab-result item that came from a filed report carries an **Open lab report →** link. It opens the chart's Labs tab with that exact report highlighted, rather than the top of the list.

An item that came in from the patient portal carries a **Reply to patient** box. Physicians, nurses, and admins can send a reply; it is stored against the request and the patient reads it in the portal under that request, with a read receipt back to the clinic. The reply is audit-logged without its text. An optional AI draft is offered here too, gated by the chart's consent, and you edit it before it goes. On a chart without AI consent the draft button is greyed out from the start and a line beside it reads "AI off — no AI consent on this chart".

An item that needs front-desk or nursing follow-up goes across with **Send to Staff Inbox**. Pick the triage action, add an instruction for staff if the action needs one, and the item lands in the Staff Inbox (§5.4) with your note attached. On a tablet or a desktop the send button is pinned to the bottom of the triage panel, so revealing the notes field cannot push it off the bottom of the screen at the moment it becomes usable. On a phone the button sits in the ordinary flow of the panel and the panel scrolls down to it once you pick a decision. The pinned version was drawn over whichever row of triage options happened to be sitting at the fold on a narrow screen, so a tap aimed at an option could land on Send and route the item instead.

**Pharmacy refill requests can be completed inline.** When a refill request is selected, the patient's active prescriptions load right in the inbox. You can select more than one medication at once, and each selected medication gets an inline editor for dose, frequency, quantity, and repeats before you approve. The medication the pharmacy actually named in the request carries a Requested badge and sorts to the top of the list, so a request for ramipril isn't as easy to mis-click as the metformin sitting beside it. When the request names a drug that matches exactly one active prescription, that one arrives pre-checked; otherwise nothing is ticked for you. Nothing is filtered out, and every active medication is still listed and can still be chosen by hand. If the requested medication doesn't match anything on the patient's active list, an amber warning tells you to verify against the chart first. Approving writes a superseding refill to the chart (audit-logged for billing) and lets you mark the item reviewed, all without opening the chart. On a refill request that came from the patient portal, **Approve & tell the patient** does three things at once: it sends the refills, replies in the patient's portal that they went to the pharmacy (naming the medications), and files the request. If the reply or the filing fails after the refills went through, the message says which step failed and what to do by hand. The request remembers what it refilled. Reopen it later and each medication already refilled from it is greyed out with "✓ Refilled" plus the time and who did it, and its checkbox is disabled. To issue another refill of that script, go through the chart.

If you do open the chart from a refill request, it opens on the **Rx** tab, the same way opening the chart from a lab result opens on **Labs**. The prescription is the thing the request is about, so that is where you land instead of on whichever tab you were last on.

The refill surface shows the same safety warnings the chart's Rx flow shows, with the same facts. An allergy warning names the allergen, the reaction, and the criticality. An interaction warning names the drug pair, the severity, and what the interaction is. The "Approve anyway" override only appears beside a warning you have actually been shown. Above the warnings, each refill row also says what the interaction screen managed to check (see the screening line in §5.8), and a refill that went through without a warning still shows that line. After you approve, the green confirmation panel lists each script that was sent, by name, with its own screening line under it. If the refill is refused because a field is wrong rather than because of a clinical warning, for example a dose you cleared, the refusal appears on that medication's own row next to the field and there is no override, because the field has to be fixed. Clearing the dose field and approving no longer quietly restores the old dose and reports success.

### 5.4 Staff Inbox
The non-physician queue. Nurses and secretaries triage administrative and clinical-support items here without needing physician note access. Tasks a physician routed across from their own inbox sit at the top of the page, above referral tracking, since that instruction is usually the reason the screen was opened. The routed note is labelled Triage instruction, because nurses triage too.

### 5.5 Messages
Internal and patient-facing messaging with threading. The nav badge shows unread count. In the message list, today's messages show the time and yesterday's show "Yesterday" with the time, so a message from late last night no longer reads as if it came in this morning. A message from earlier in the week shows its weekday, except when that weekday is the same as today's, where it shows the date instead, so a six-day-old message is not mistaken for one from this morning. Patient-linked messages also appear under the chart's **Messages** tab. When a message references a chart item, the reference is shown as a chip that opens the real thing: a lab chip jumps to the chart's Labs tab and highlights that report, and an Rx chip opens the Rx tab with the medication highlighted. Where the chart holds the same drug more than once, the chip lands on the active script rather than on a cancelled or completed one, so a message about a current medication no longer flashes a script that was stopped months ago. If the linked item has since been removed, a toast says so instead of a dead end.

### 5.6 Patients and the Patient Chart
Select **Patients**, search by name/identifier, and open a chart (or use the global patient search in the top header from any page). The chart header carries **Edit info**, **Message** (start a message linked to this patient), **FHIR R4**, and **Export PDF** controls. The chart opens with a **clinical banner** across the top showing active allergies, active medications, and active problems at a glance, always visible so you never miss a contraindication. A chart with no allergy entered reads "Allergies not recorded" in the banner, on the Allergies card, and in the PDF export. That is a statement that nothing has been entered, and it is deliberately not "No known allergies", which is a clinical finding somebody has to record.

The **…** menu at the right of the header holds one action, **Archive** (or **Unarchive** on an archived chart), with an in-app confirmation. Starting a visit on an archived, inactive, or deceased chart is refused by the server with the chart's status named, and the chart asks "Start an encounter anyway?" The confirmed start is written to the audit log as an exception.

The intake link can be sent again or withdrawn later from the chart's Forms tab, on the New-patient intake form card (physicians, nurses, secretaries, staff, and admins). **Email intake form** sends a new single-use link to the address on file and stops any open one. **Revoke link** stops an open link at once, for example one sent to the wrong address; the revocation is audit-logged. When the patient completes one link, any other open link for them stops working too.

Registering a patient with the intake form ticked now tells you when the invitation could not be created. The patient is still saved; an amber notice under the form says the intake invite failed and what to do about it, instead of the failure being swallowed. The confirmation panel also states the AI consent you recorded accurately: with Yes recorded, the patient's answer on the intake form will replace it; otherwise AI stays off until the patient answers Yes on the form or consent is recorded in the chart.

**Edit info** opens the demographics window: family name, given names, preferred name, date of birth, sex at birth, phone, email, and health card with its version code. Physicians, nurses, and admins see the button. The health-card field shows "On file — leave blank to keep", so you can correct an address or a spelling without retyping the number. Until this shipped there was no screen anywhere for fixing a typo made at registration.

An Ontario health card number is checked when a patient is registered and when it is edited. It has to be 10 digits (spaces and dashes are fine) and its last digit has to pass the card's check-digit rule. The version code, if there is one, is the two letters printed after the number. Leave the number blank for a patient without an Ontario card. A number that fails reads "Health card number is not valid (check digit does not match) — re-check the card." The server applies the same rule, and its refusal never repeats the number back.

A phone number has to be one you could dial: 10 digits, written any common way (416-555-0123, (416) 555-0123, +1 416 555 0123), with an optional extension such as x123. The area code cannot start with 0 or 1, and it cannot be a service code such as 411 or 911. A pharmacy fax number also cannot be a premium-rate one (area code 900, or a 976 exchange). The registration form saves it in the 416-555-0123 shape. Anything else is refused on registration and in Edit info with "Phone number '…' is not valid: enter 10 digits (for example 416-555-0123), optionally with an extension (x123)." The server checks it too.

Invisible characters pasted along with a name (zero-width spaces, a stray byte-order mark, soft hyphens) are removed when the patient is saved, so the name matches what you see when you search for it later.

The chart header shows a baby's age in days under one month and in months under two years, so a newborn no longer reads "0y". A very long name is cut short with an ellipsis in the header (hover for the whole name) instead of pushing the header controls off the screen.

A date of birth that could not have happened is refused on both the registration form and the edit window: a date in the future, or one more than 130 years ago. The server checks it too, so the rule holds whether or not the form was used. A birth date that makes the patient older than 120 is allowed but asked about once on the registration form: the message gives the age, asks you to check the year, and says to press **Create patient & open chart** again if it is right. Age drives billing eligibility, screening and recall windows, and paediatric dosing, so a wrong birth date is not only a wrong number in the header.

The birth date also bounds the rest of the chart. A clinical date is refused if it falls in the future, and refused again if it falls before the patient was born, with a three-day allowance so a reading taken on the day of birth still saves. That covers vitals, immunizations, and the other dated clinical entries, on the server as well as in the form. A weight dated 1899 on a patient born in 1945 used to save and sit at the bottom of the vitals history.

When a save is refused, the message you see is the server's own reason, including a field-level one such as a date it would not accept. A refusal used to be able to leave a blank screen or a generic "try again".

A vital recorded in error can be taken back. Every row in the Vitals history carries a Retract control for physicians, nurses, and admins. It asks you to confirm, naming the reading and the date it was recorded. Escape or a click outside the box cancels without changing anything. Confirming marks that reading inactive so it drops out of the chart and the trends. Nothing is deleted. The original entry and the retraction both stay in the audit log, which is what PHIPA s.55 expects of a correction. Re-enter the right value afterwards. The Vitals form refuses a height under 10 cm or a weight under 0.1 kg before it sends anything. A height of 1.75 is read as metres typed into the centimetre box, and the message asks whether you meant 175 cm. A BMI it cannot compute is left blank, with no weight category against it. If you save several readings at once and one of them is refused, the ones that saved are cleared from the form and named in the message, so pressing Save again after the fix does not record them twice. Its placeholders read "e.g. 72" so an example is not mistaken for an entered value. A temperature above 42 °C needs an explicit "confirm this extreme temperature" tick before it saves (the 30 to 45 °C hard bound is unchanged); a refused save scrolls to that tick and marks it red. A weight under 2 kg on a patient older than one year works the same way: the message gives the weight and the age, and the reading saves once you tick "Confirm this weight is correct".

Registration will not save a patient when the intake form is set to be emailed and no email address has been entered. The checkbox reads "Email the new-patient intake form to this patient" and it needs an address to send to, so the form asks you to add one or untick the box. Until this shipped the patient was created, no form went out, and nothing said so, which left the front desk waiting on an intake the patient was never sent.

On the Immunizations tab, the dose number in a series has to be a whole number from 1 to 10. Anything else is refused with "Dose # must be a whole number from 1 to 10." A 0 or a negative number used to save and would have produced an invalid FHIR export. Physicians and admins see a Remove control on each dose; other roles don't see it, because the server would refuse them. Remove asks for confirmation in the app and explains that the dose leaves the patient's record but the entry stays in the audit trail. Use it for doses recorded in error.

A Summary card with nothing in it says so in its own words, for example "No family history recorded" or "No risk factors recorded", so a blank card reads as an empty list rather than as something that failed to load. Saving or retracting a vital refreshes the Recent Vitals card on Summary as well, including when Summary is pinned beside the Vitals tab, so a withdrawn reading does not linger there.

Recording an allergy reads the reaction you type. If it names a reaction that usually means high criticality (anaphylaxis, angioedema, Stevens-Johnson syndrome or TEN, DRESS, or airway trouble such as stridor or throat swelling), the form selects High and says why. You can change it. If you have already set Low by hand, the form leaves it and points out that the reaction usually means High.

A new allergy is also checked against the patient's active prescriptions, using the same matching the prescribing screen uses. If one of them collides, for example amlodipine on the list and "Amlodipine — ankle edema" just recorded, a red box on the Allergies card names the drug and offers **Hold**, **Discontinue**, or **Leave as is**. Nothing changes until you choose, and each choice is audit-logged. Holding and discontinuing a script are physician and admin actions. On the Rx tab, an active script that collides with an allergy carries a red ⚠ Allergy badge in the list, and a held script has a **Resume (end hold)** button.

Removing an allergy asks why, because there are two different answers. **No longer allergic** means the allergy was real and has resolved (a tolerated re-challenge, desensitisation); it stays on the record as a resolved allergy. **Entered in error** means the wrong patient or the wrong allergen; it is marked entered-in-error and is never screened against or summarised again. The button reads **Mark resolved** or **Mark entered in error** to match your choice, and there is an optional reason box. The reason is kept on the allergy; the audit row only notes that one was given. An allergy marked entered in error cannot be switched back to active on its own. Correct it in the same edit that fixes its verification, or record it again.

The care team card lists the patient's specialists and other providers. Physicians, nurses, and admins can take a provider off it with the × on their row. The confirmation says the provider is marked as no longer involved as of today; the row leaves the card but stays in the chart history with its end date. A FHIR export carries the current members in an active CareTeam and the ended ones in an inactive CareTeam with their end date.

A family-history entry's age at onset has to be between 0 and 120.

Diabetes and hypertension flowsheets exist on the server but have no screen yet. Each one pulls the latest value of every element from labs, vitals, and hand-recorded entries and compares it with guideline targets and testing intervals. Physicians and admins can set a patient's own target with a reason; an old target is superseded and kept, never deleted. The Flowsheets flag (§5.15) switches them off.

The chart is organized into tabs. A tab can be pinned so it docks as a side panel beside whatever tab you are reading; drag the panel's divider to resize it, and the width is remembered. The pin icon on a tab opens a short menu:

- Pin on this chart only. The pin applies to this patient, for you, in this browser.
- Pin as my default (all charts). Every chart you open on a desktop shows that panel. The default is saved to your account, so it follows you to another workstation.
- Unpin on this chart, or Unpin on this chart only when the pin came from your default. The default stays in place for other charts.
- Remove my default pin, and Use my default here when a chart-only choice is hiding your default.

While you are on the tab that is pinned, the side panel is hidden, so the same tab is not shown twice side by side. A pin belongs to the person who set it. On a shared workstation a nurse no longer opens a chart and finds the physician's pinned panel. The default is applied on desktop widths only, so the default options are not offered on a phone. An admin can switch the personal layout features off for the practice (§5.15), which brings back the older one-click pin.

The tabs:

| Tab | What it holds |
|-----|---------------|
| **Summary** | The Cumulative Patient Profile (CPP): problem list, medications, allergies, past medical/surgical history, social history, preventive care, the one-screen clinical picture. The Active Medications panel lists what is actively prescribed here first, then anything held under *Documented · not prescribed* (reconciled meds and accepted SKIPPY suggestions that were never sent to a pharmacy), the same split the Rx tab uses. Problem-list items can be edited inline (relabel / re-date). Also carries the patient's **Preferred Pharmacy** card (view/edit), which prefills the eRx fax modal |
| **Encounters** | The encounters hub — every visit, with the SOAP note(s), prescriptions, referrals and forms done in it. This is where you start, document, sign, and bill an encounter (see §5.7) |
| **Rx** | Prescriptions — create, renew, with drug-interaction checking |
| **Vitals** | Vital signs with trending charts (BP, weight, etc.). A reading outside the accepted range is refused with the range named, and a diastolic at or above the systolic is refused as a likely transposition. A height under 10 cm or a weight under 0.1 kg is refused by name, and a BMI that cannot be computed is left blank with no weight category against it. Each row in the history carries a Retract control for physicians, nurses, and admins. Blood-pressure rows carry mmHg |
| **Imaging** | Diagnostic imaging / DI reports. Clinicians get a **+ Order imaging** button here for this patient |
| **Immunizations** | Vaccination record with standard CVX vaccine codes |
| **Labs** | Lab results with trends; acknowledge and set follow-up urgency. Clinicians get a **+ Order bloodwork** button here for this patient |
| **Inbox** | Inbox items filed to this patient, split into an **Open / Archived** toggle (reviewed and archived items move to Archived) |
| **Letters** | Correspondence — referral and consult letters, with attachments |
| **Docs** | Uploaded patient documents (drag-and-drop a PDF/JPEG/PNG/TIFF up to 25 MB anywhere on the chart to file it); a document row expands **in place** into an inline PDF viewer, so the chart stays visible |
| **Forms** | Clinical forms for this patient — clinic-authored eForms and official fillable government/agency PDFs; prefill from the chart, print, and fax/email. The New-patient intake form card at the top emails the intake link, lists every link sent (Open, Completed, Revoked, Expired), and has **Revoke link** on an open one |
| **Billing** | This patient's billing history |
| **Messages** | Messages linked to this patient |

![Screenshot — patient chart Summary tab showing the clinical banner and Cumulative Patient Profile.](images/screen_2_chart_summary.png)

*Screenshot — the **Summary** tab: clinical banner across the top, then the CPP (problem list, medications, allergies, history, social history). Note the tab strip and the Edit info / Message / FHIR / Export controls in the header.*

### 5.7 Encounters — the hub of the chart (and how notes live inside them)

EMR21 is built around encounters. A clinical note lives inside an encounter (a visit) rather than floating on its own. The chart's Encounters tab is where visits live, and each visit gathers what happened in it: the SOAP note or notes, prescriptions written, referrals sent, and forms completed. Because a visit is a real container, every clinical action in it has a date, a responsible clinician, an audit record, and a claim to bill against.

#### Why encounters exist
Encounters exist for three practical reasons. First, one visit means one record: the note, scripts, referrals, and forms from a sitting sit under one dated encounter instead of being scattered across tabs, so anyone reading the chart later can see what happened. Second, it satisfies PHIPA. Starting an encounter creates an owned container, and every open, read, and change to it is logged (see "How it logs" below). Third, it is what you bill. Closing an encounter captures the OHIP code and creates the claim, so visits don't get left unbilled.

#### The encounter lifecycle

![Encounter lifecycle — planned, arrived, in-progress, finished or cancelled; the four ways a visit starts; and how each step is logged.](images/03_encounter_lifecycle.svg)

*Figure 3 — Encounter states, how a visit starts, and what gets logged.*

Encounters follow the FHIR status flow **planned → arrived → in-progress → finished** (and **cancelled**). In day-to-day use you mostly see three states: not started, **active (in-progress)**, and **finished/billed**.

There are several ways an encounter begins — you rarely have to think about it:

1. **Start encounter button (Encounters tab).** Shown to physicians, nurses, admins, and the learner roles. The Encounters tab carries a **▶ Start encounter** button; click it and an `in-progress` encounter is created on the spot (works for walk-ins and direct chart access — no need to go through the scheduler). On an AI-consented chart a second button, **✦ Start + AI Scribe**, starts the visit and opens dictation in one step: the AI drafts the note, you review and sign. Start, end, and cancel all live in the Encounters tab; while a visit is active the chart header shows a read-only violet **"Encounter active"** status pill with a pulsing dot.
2. **Auto-start when you document.** In the Encounters hub, the **"+ New note · starts a visit"** button will automatically open today's encounter (if none is active) and drop a new note into it — so you never have to remember to start one, and billing always has a visit to attach to. If you already have a visit open for a *different* patient, EMR21 warns you before starting a second one (your first visit stays open until you return to bill it).
3. **SKIPPY reminder.** If you open a chart as a physician with no active encounter, SKIPPY shows a proactive "start the encounter" reminder (so the visit is captured for billing and audit). You can start it straight from the reminder. The reminder clears when you leave the chart.
4. **From the Schedule.** Pressing **Start Encounter** on today's appointment (physician, nurse, or admin; the front desk marks Arrived only) creates an `in-progress` encounter for that patient, or hands back the one you already have open on that patient today, so a scheduled visit always has its container the moment it begins.
   Bloodwork ordered from the Orders worklist while you have an encounter open attaches to that visit, and lab and imaging orders count as visit actions in the end-of-visit summary and billing rationale. While a visit is open, the encounter bar carries **+ Order bloodwork** and **+ Order imaging** buttons (on a tablet-width screen or wider) for physicians, nurses, and admins. They open the same order windows the Orders page uses, with the patient in the room already filled in.
5. **Review encounters.** Reviewing results, inbox items, or referrals without a face-to-face visit can be logged as a lightweight **Review** encounter (created already-finished, marked "Results / inbox review"). This means even non-visit chart work leaves an audit trail and can be billed. Review encounters are tagged with a **Review** badge in the list.

**One open visit per clinician, per patient, per day.** Whichever door you come through, EMR21 will not stack a second in-progress encounter on a patient you already have open today. A page reload that lost its place, a second browser tab, or starting from the schedule and from the chart all give you back the same visit instead of a duplicate. The rule is scoped to you. A nurse who rooms the patient and opens a vitals encounter and the physician who then starts the visit each keep their own encounter, with their own reason for visit and their own name on it. A visit left open on an earlier day is never reused silently, because that would date today's work and today's claim to the earlier day. When you start an encounter and one of yours from an earlier day is still open on that patient, a dialog headed **An earlier visit is still open** names the date and gives you two choices: **Close it as incomplete (not billed) and start today's**, or **Resume** that earlier visit. Closing marks the old visit finished as incomplete with no claim, records the close in the audit log, keeps its notes in the chart, and shows no duration for it (the real end time is unknown). Resuming keeps the old start time, and the timer shows it. Not now leaves everything as it was. You can also pick an old visit back up deliberately with **Resume** in the visit list.

#### The Encounters hub UI

![Encounters hub wireframe — left pane is the visit list (active visit pinned with "End & bill", past visits with billing/Review badges); right pane is the SOAP note editor or visit summary.](images/04_encounters_hub.svg)

*Figure 4 — The Encounters hub: visit list on the left, note editor / visit summary on the right.*

![Screenshot — the Encounters hub with a visit selected, showing its signed note and Bill visit button.](images/screen_3_encounters_hub.png)

*Screenshot — the live Encounters hub. Left: the **Start encounter** / **Start + AI Scribe** buttons and the visit list with OHIP codes, **Review** badges, and durations. Right: the selected visit's summary — signed note, prescriptions, referrals, and the **Bill** button with its OHIP code.*

The Encounters tab is a two-pane view:

- **Left pane — the visit list.**
  - The **active visit** is pinned at the top with a green header, a pulsing dot, the reason for visit, small icons showing what's been done (note 📝, Rx, referral, form), and its note(s). It carries an **"⏹ End encounter & bill"** button.
  - **Past visits** are listed below, newest first, each showing the date, a **Review** badge where applicable, the **billing code** badge (or an amber dot meaning *not yet billed*), the duration, and the reason. Expand a visit to see its notes and the scripts/referrals/forms done in it, add another note, or hit **"Bill this visit."**
  - **Stuck in-progress encounters can be recovered in place.** A visit that is still *in progress* but isn't your locally active one (e.g., after a browser restart or an interrupted close) is tagged with an amber **In progress** badge and offers **▶ Resume** (re-attach it as your active visit) and **⏹ End** (close it without billing — the manual path) alongside the usual Bill action, so no encounter is left open by accident.
  - Legacy notes that predate the encounter model appear at the bottom under **"Unfiled notes"** (display only).
- **Right pane.** Shows the **note editor** when you're writing/editing a note, or a **visit summary** (everything done in that encounter, each item linking out to its tab) when you select a past visit. After you bill or close a visit through the billing drawer, the pane refreshes itself — the new billed/finished state appears without a manual reload.

#### Notes inside an encounter
Within an encounter, notes work as before: structured **SOAP** (Subjective, Objective, Assessment, Plan) with free-text and dictation support — the editor can parse dictation into the SOAP structure, and the AI Scribe can transcribe recorded audio. A note is a **draft** while unsigned (editable). When you start a new note you can either **Save draft** (finish it later) or **Create & sign** in one step. When you **sign** a note, EMR21 takes an **immutable snapshot** (with your identity and timestamp) and it can no longer be edited; to change a signed note you create an **amendment** that links back to the original, which is never overwritten. Notes can also be flagged **sensitive** to exclude them from sharing. Signing a note is also what triggers SKIPPY's post-encounter chart suggestions (§5.16).

When the AI Scribe drafts a note, each assessment line is checked against the dictation after the model answers. A line whose distinctive words never appear in what was said is kept but prefixed "[inferred — not stated in dictation]", and the editor shows an amber pill counting those lines so you review them before signing.

Two more checks run on a scribe draft. A lab value or vital that the draft took from the chart and that was not spoken in the dictation is tagged in the text with its source and date, for example "(lab 2026-06-12)" or "(vital …)", so an old potassium is not read as today's. A blue pill in the editor counts how many values were tagged. The dictation and the draft are also screened. If the dictation contains text that reads like an instruction to the software, or the draft claims something was already done ("prescription sent"), an amber notice explains what was found and asks you to confirm it before signing. The notice never blocks the note.

The scribe writes a patient's request as a request. "Patient requests a referral to dermatology" stays that way in the draft, and it is not turned into "referral sent" or "referral ordered" unless the dictation says the clinician did it.

A note drafted by the AI Scribe records that fact. After you sign it, the note shows an "AI-drafted · reviewed" badge (hover: drafted by the AI Scribe; reviewed and signed by the physician). The mark cannot be removed by later edits.

Writing and signing a note is limited to physicians and admins, plus residents and clinical clerks working under a preceptor (below). A nurse still starts, cancels, and closes encounters, but gets no note editor and no **+ Note** button inside one. An encounter of theirs with no note reads "No note yet — notes are written by the physician." Findings go in vitals or a message to the responsible physician. If a nurse reaches note signing some other way, the server refuses with a sentence saying only physicians and administrators can create and sign a clinical note.

If a recording holds no speech, the scribe says "No speech was detected in that recording. Try again, a bit closer to the mic." instead of drafting from background noise.

What a secretary or staff login sees of a note depends on the practice's front-desk note access setting (§5.15). With the default, metadata only, they see each note's type, status, author, and dates but never its text, and the row says why. With access set to none, the Encounters tab says clinical notes are not available to their role at this practice. The chart PDF export follows the same setting.

Text you have typed into a note and not yet saved survives a switch to another chart tab. Go to Labs to check a value, come back to Encounters, and the draft is still in the editor with a note saying your unsaved text was kept. It is held in memory for your login only, never in browser storage, and it is cleared when you save, sign, discard, or log out. Save or sign to keep it for good. A page reload or a closed tab is different: the browser asks whether you want to leave while a note has unsaved text, and only then. Discarding a draft or inserting a template over one asks in an in-app dialog first.

The empty Encounters tab says to use the **Start encounter** button above to start a visit, so a new chart tells you where to begin.

Cancelling an encounter started in error is open to physicians, nurses, and admins. A resident or clinical clerk can cancel, or close without billing, an encounter they started themselves, and nobody else's. If the cancel is refused, the encounter chip stays in the header and the dialog says the encounter is still open, naming who can cancel it. Previously a refused cancel looked like it had worked.

#### Notes written by a resident or clinical clerk
A resident or clinical clerk documents under a preceptor, who must be an active physician in the practice. When a learner opens the Encounters tab, a banner at the top says who their preceptor is ("your signed notes go to them for review"). With no preceptor yet, the banner reads "No preceptor yet. Ask a physician to supervise you before documenting." Pick a physician from the list, add a reason if you like, and press **Request**. The request goes to that physician's inbox and Dashboard, and the banner reads "Request pending with …" until they press **Confirm** or **Decline**. A learner cannot save or sign a note until a preceptor has confirmed; the server refuses with "Set a preceptor before documenting." A physician or admin can also set or end a learner's preceptor directly through the API.

When a learner signs, the note does not become signed. It moves to Pending review, is locked against further edits, and its footer reads "Signed by the learner … · awaiting preceptor review". The reviewer is fixed at that moment, so changing preceptors later does not move the note to someone else. Only that preceptor co-signs. Any other physician reads "Awaiting the preceptor's review". The preceptor's review bar has a comment box and three buttons:

- **Return with revisions** sends the note back as a draft. The learner's text is kept and their signature is cleared, and an inbox task headed "[Learner review] Note returned with revisions" reaches the learner with your comment. A comment is required.
- **Sign with comment** co-signs the note and keeps your comment on it. A comment is required.
- **Sign** co-signs the note as it stands.

An admin sees the bar with **Return with revisions** only and a line reading "Only the learner's preceptor can co-sign. You can return it, or reassign the preceptor." The server refuses a co-signature from an admin as well. A co-signed note shows "Co-signed by" the preceptor's name and role, with the time and any comment, under the learner's signature line. A "Written by" line names who wrote which part, for example "the resident (free text, assessment) · the physician (preceptor comment)". A note the AI Scribe drafted is marked reviewed by the co-signature, never by the learner's own signature. Every request, confirmation, review, and return is audit-logged. An admin can switch the review surfaces off with the Learner supervision flag (§5.15), but a learner's signature still always lands in Pending review.

#### How an encounter logs things (audit & billing)
- **Audit trail (PHIPA).** Creating, opening, listing, updating, and closing an encounter each write an append-only `audit_log` entry — who, the action (CREATE/READ/UPDATE), the encounter ID, status changes, and the originating IP. Once an encounter is **finished or cancelled it cannot be modified**, which keeps the record defensible.
- **Billing.** When you end an encounter and confirm the OHIP code (via the billing drawer, §5.13), the encounter is marked **finished**, its end time is stamped, the confirmed code is stored on the encounter, and a **draft billing claim is auto-created** (or updated) so the code flows straight into Billing. The encounter window is also what SKIPPY reads to suggest that code in the first place.

### 5.8 Prescriptions (Rx)
Create and renew medications from the **Rx** tab. Each active medication in the left pane shows when it was **started** and when it was **last prescribed**, so renewal decisions have their dates in view. Medications carry coded identifiers (RxNorm/ATC) under the hood for interoperability.

Three places on the Rx tab show a refill count, and each one now says which count it is. The detail grid says **Refills authorized**, meaning what the script allowed. The **Estimated supply** card below it says how many are still unused, for example "1 refill remaining". The row in the medication list says "3 refills authorized" next to the prescribed date. All three used to read just "refills", so a script showing "Refills 3" could sit directly above "no refills left" and the pair looked like a contradiction.

Each script in the list also shows where it went, without your having to open it: Faxed, Queued (recorded for fax, waiting on eFax being switched on), Printed, Pending where the patient will arrange it, or Documented for a medication started elsewhere. Hover the marker for the longer wording. A script that was printed and handed to the patient no longer describes itself as queued for fax.

Reading the Rx tab is open to physicians, nurses, staff, admins, residents, and clinical clerks. A secretary opens it and reads that prescriptions are restricted to those roles, rather than an empty list. The empty list was the more dangerous screen: a chart with five active scripts read as none prescribed, and the Summary tab relabelled the same medications *documented, not prescribed*. Writing a script is narrower again, physician or admin.

Where a drug is picked from the catalogue and that entry carries a Health Canada DIN, the script stores the DIN together with the code system that defines it. Both halves travel or neither does. That pair is what the FHIR R4 export puts on the MedicationRequest, so a receiving system reads a coded drug rather than a name in text. Re-prescribing a past medication by name drops the catalogue link, and drops the coding with it, rather than carrying a code the script is no longer asserting.

**Interaction checking and safety rails:**

- A prescription cannot be signed without a dose. The dose field is marked required and turns red with the reason inline before you press Sign & Print, rather than after. It only turns red once a drug has been picked from the catalogue or the Dose field has been visited, so an untouched blank form, or one where you are still typing a free-text drug name, doesn't look like an error. This is a completeness check and says nothing about whether the dose is right: if the directions carry the amount, write "as directed" and the script goes through. Modify is the repair path for an older script that was saved without one.
- A dose or frequency that has been run together is refused. When the catalogue prefills Dose and Frequency and you click into either field without having changed it, the whole value is selected so that typing replaces it rather than appending to it. If a value like "500 mg250 mg" or "BID PRNBID" still reaches the server, the script is refused on create, modify, and update with the reason, and an older script already stored that way is refused at fax or print until it is corrected with Modify.
- Every new script is screened against the patient's allergy list as well as their medications. Allergies typed straight into the chart's Allergies & Intolerances panel are screened, not only ones imported with a category on them. Matching is by allergen name against the drug name and its class.
- Drug-interaction checking runs on every new script against everything the patient is actually on: the medications recorded on the CPP and the active prescriptions written in EMR21, deduplicated so one drug isn't screened twice.
- The interaction reference lists specific drug pairs, and it also describes those same risks by drug class. Screening reads both. Where the reference calls a DOAC with an NSAID a major bleeding risk, apixaban with celecoxib raises that warning even though nobody wrote that particular pair down. A warning matched this way says so on its face, so you can tell it apart from an enumerated pair. Class matching only ever adds a warning; it never removes one. One interaction raises one warning. A chart that holds the same molecule twice, once as a CPP medication and once as a prescription written here, no longer produces a second warning claiming the pair isn't in the reference beside the first one saying it is.
- Two antithrombotics from different classes raise a major bleeding warning. Warfarin with a DOAC (apixaban, rivaroxaban), warfarin with clopidogrel, and a DOAC with clopidogrel are each flagged in both directions. Until 2026-09-22 these combinations screened clean. The warning asks you to confirm the indication and a stop date, for example a planned switch from warfarin to a DOAC or antiplatelet therapy after a stent. Like every interaction warning it is advisory, and you can prescribe past it with a reason, which is audit-logged.
- Some risks only exist when three drugs are on board together, which a pairwise interaction table can't hold. The triple whammy is checked in its own right: an NSAID plus an ACE inhibitor or ARB plus a diuretic raises a major warning for acute kidney injury, naming which drug fills each leg. It fires only when the drug you are prescribing is one of the three, so it doesn't warn about a combination you aren't changing. Low-dose ASA doesn't count as the NSAID leg.
- The drug is also checked against the conditions on the patient's problem list. Five pairs are covered today: an NSAID in chronic kidney disease, an NSAID in heart failure, metformin in renal impairment, a beta-blocker in asthma, and an anticholinergic in dementia or cognitive impairment. The warning is headed Condition-Based Contraindication, carries its severity, and quotes the chart entry that fired it, so you can see the reasoning, judge it, and correct the entry if it is wrong. A beta-blocker is graded on its selectivity: a non-selective one in asthma is major, a cardioselective one is a moderate caution with advice to start low and check that a reliever is current. A relative's illness, a denial, a query, and a "rule out" are read as not the patient's current problem and do not fire a warning. An entry whose status is unknown is screened rather than dropped.
- Kidney function is read from the labs as well as from the problem list. The warning prints the most recent eGFR result with the date it was taken. Below roughly 30 the warning is major; 30 to 59 is a dose caution, with the usual metformin advice to cap the daily dose and recheck renal function every three to six months. A low result raises the warning even when nothing renal is written on the problem list, which is the common case. The lab can only ever make a warning stronger or add one: a reassuring result never switches off a warning the problem list is asking for, because a lab value can be stale, transient, or filed against the wrong patient. Where the chart and the lab disagree you are shown both. The line under the warning says which of the two triggered it, the problem-list entry or the most recent lab value, and says explicitly when the trigger was a lab value rather than something on the problem list.
- Condition screening runs on a drug you type by name as well as one picked from the catalogue. It is advice and never a barrier: you can proceed, optionally recording why. The override, your reason, and the contraindications you went past are written to the audit log on new scripts, refills, and dose changes alike. The refill and modify dialogs show the same warning the new-prescription screen shows, with the severity and the full clinical text, and offer the same place to record why you are proceeding.
- If the patient already has an active medication of the same molecule, prescribing it again raises a **duplicate-therapy** warning (for example, ordering rosuvastatin when rosuvastatin is already on the active list). It reads the same list the interaction check reads: the medications recorded on the CPP plus the active prescriptions written in EMR21. A patient whose drugs live only in prescriptions is screened the same as one with a full CPP. The warning is headed "Same-molecule duplicate", names the molecule, and says to prescribe only if a second concurrent script is intended and to modify the existing prescription instead if the aim is a dose change. You can override it; the override is audit-logged. The check only ever adds a warning, it never blocks silently.
- A second drug from the same class raises its own warning, headed "Class-based duplicate" and naming the class. The classes covered are NSAIDs, SSRIs, SNRIs, statins, ACE inhibitors, ARBs, benzodiazepines, beta-blockers (metoprolol over bisoprolol, for example), and opioids (codeine on top of tramadol), plus two cross-class pairs: an SSRI with an SNRI (serotonergic load) and an ACE inhibitor with an ARB (dual RAAS blockade). Fluoxetine written over active sertraline is the case it was built for. A brand name on the active list is classified by its catalogue entry, so Lipitor counts as a statin. Low-dose ASA is not counted as an NSAID. The warning carries the same override and audit row as the others.
- Cephalexin, cefadroxil, and cefaclor share a side chain with amoxicillin and ampicillin. Prescribing one of them to a patient with a high-criticality or anaphylactic penicillin allergy raises an amber Side-chain advisory suggesting a cephalosporin with a different side chain. It is a confirm, not a block.
- Both duplicate checks also run on a refill and on Modify. The script being replaced is left out of the comparison, so refilling ramipril does not warn that the patient is already on ramipril.
- On a chart where no allergy has ever been recorded, signing a script stops on an amber prompt, "Allergy status not recorded — confirm with the patient before signing", with **Confirm and sign** and **Go back**. An empty list is not a statement of no known allergies. Ask the patient and record what they say, or confirm and sign; the confirmation is audit-logged. It is a prompt, never a block.
- Drug-to-drug interaction screening only works on drugs EMR21 can match to its reference catalogue. If you type a free-text drug that isn't in the catalogue, or if none of the patient's current therapies can be matched to it, EMR21 raises an "unscreened — verify manually" advisory that you must acknowledge before prescribing (the acknowledgment is audit-logged). An unscreened list never comes back looking like a screened one.
- A one-line screening note says how complete the drug-to-drug interaction screen was, and it starts with "Drug–drug:" so it is not read as covering allergies too. Until a drug is chosen the form reads "Choose a drug to screen — pick it from the list, or finish typing the name and leave the field." Half a name is never screened, so a partial "Citalo" can't show a green line. The note appears on the Rx form once a drug is chosen (before you sign), in the override dialog, on the Modify warnings, in the refill dialog, on the Send step, and on Inbox refill rows. Green reads "Screened against 4 active medications — no interactions found." Amber reads "… — 2 warnings", or "Partially screened" when some of the patient's medications are not in the interaction reference; that line names them, and hovering lists them. Red reads "Not screened" with the reason. The count is of drugs, not chart rows: metformin recorded on the CPP and a metformin script written here count as one medication. When an allergy or allergy-status warning sits below a clean drug-to-drug line, the line is shown in grey instead of green. The line is information only. It does not change which warnings appear or how overriding works. When you change the drug on the form, the old drug's line disappears at once. The Screening honesty states flag (§5.15) hides the line, but the state is still written to the audit row of every prescribing action.
- The drug picker says which of those you are in. Type two characters and the dropdown opens either way: it reports that it is searching, lists the catalogue matches, or says that no catalogue drug matches what you typed and that you can still type the name in full to prescribe it. That message names the two kinds of screening separately, because they behave differently on a typed name: allergy and condition-based screening still run, drug-to-drug interaction screening cannot, and the script is flagged unscreened. A search that can't reach the server says that instead. The catalogue is a working subset, so a routine drug missing from it is a gap in the reference data rather than a bar on prescribing. Twenty common family-medicine drugs were added in October 2026 (iron, cephalexin, metoprolol, and tramadol among them) along with 43 interaction pairs that involve them. These entries carry no DIN.
- A dispense quantity that cannot be dispensed is refused rather than saved: zero, a negative number, or an absurdly large one. So is a repeat count above the form's own maximum of 11. Quantity stays free text, so "one inhaler" or "100 mL" is fine; the check only reads a number when the entry contains one.
- Changing the drug on the Rx form **clears any auto-filled dose/route/frequency/quantity — and any auto-filled Limited Use (LU) code and criteria** — so a previous drug's dosing or LU justification can't carry over by accident. Re-prescribing a past medication likewise starts with clean LU fields: you re-confirm Limited Use for the new script rather than inheriting stale criteria. The same goes for a form filled from a re-prescribe or from a documented medication: change the drug name and its dose, frequency, quantity, and directions are cleared.
- Refilling or modifying a script that is no longer active (held, cancelled, or already replaced) is refused with a sentence naming its status and, where there is one, the script that replaced it.
- Pressing Sign or Prescribe anyway twice, or a retry after a lost connection, writes one prescription.
- If the browser has lost track of the visit you have open (after a reload on another machine, say) but the server still holds an open visit for this patient today, the New Rx form asks "Attach to today's visit (10:40)?", ticked by default. Untick it to write the script outside the visit. A script is never attached silently.
- Changing the dose, route, or frequency on an existing medication brings the directions along with it. If the directions are still the ones EMR21 generated, they are rebuilt from the new values. If you wrote your own wording, only the changed value is substituted, so an instruction like "with food" survives. If the directions still state the old strength after that, they are cleared rather than left wrong, and the audit row records that. Directions are never invented, and a refill changes nothing clinical so it leaves them alone.

EMR21 can also suggest one to three prescriptions from a visit note's assessment and plan. Reading the note is a read of that patient's chart, so the suggestion runs only where AI consent has been granted. On a declined or unset chart the request is refused before any note text reaches the model, and you prescribe the usual way.

**Documented, not prescribed.** Medications recorded on the chart without a script, including ones accepted from SKIPPY's post-encounter suggestions, appear in their own Rx-tab section labeled *documented, not prescribed*, each linking back to its source note, with a **Prescribe & send** action so an AI-suggested medication is never missed at script review. The Summary tab's medication panel carries the same split, so the distinction is visible on the first screen you read in a visit. A documented dose that disagrees with the prescribed one stays on screen as its own row rather than being folded away, because a reconciliation discrepancy is something to see.

Every signed or refilled script ends with a send step. After you sign, override-sign, or refill, a **Send prescription** modal opens and asks where the script is going. The options are fax the pharmacy on file, fax a different pharmacy, print and hand it to the patient, defer so the patient arranges it (the script is marked pending rather than quietly active), or document only for a medication started elsewhere that should not be transmitted. Whichever you pick is recorded on the script and audit-logged. eFax is not switched on yet, so choosing fax records the send and is ready to transmit once it is.

Every option except print confirms in the dialog for a couple of seconds before it closes, so you can see what was recorded. Faxing reads "Recorded as faxed to [pharmacy]. eFax isn't enabled yet, so nothing has been transmitted." Deferring reads "Marked pending", and document-only says nothing was transmitted. Print closes straight away, because the print dialog is its own confirmation.

If the patient has no preferred pharmacy on file, the modal says so and offers to fax one, with a checkbox to save it as this patient's preferred pharmacy for next time. A fax number has to be a dialable North American number and not a premium-rate one; a bad number is refused with the reason, and the number and pharmacy are stored on the script and in the audit log.

Holding or discontinuing a script whose fax is still queued withdraws the fax. The script's delivery marker then reads Fax withdrawn, and hovering it says to call the pharmacy if the fax had already gone. If **Resume (end hold)** fails, the reason appears beside the script. The preferred pharmacy can also be set or edited on the Summary tab, and it prefills the fax option.

### 5.9 Labs
Review results in the **Labs** tab with trend charts for repeated tests. Each result shows its reference interval next to the value, composed from the low and high bounds when the lab sent no written range. Not every result is a number. A culture or a nucleic-acid test answers in a word, and that word now appears in the value column where the number would sit, so a positive chlamydia reads as Positive rather than as a dash beside a red flag. Word results also carry through to FHIR export and into the chart context a note draws on. When you review a result, set its follow-up disposition: **Normal**, **Review at next visit**, **Notify patient**, or **Order follow-up**. Acknowledge sits in the report header. A long report repeats it at the foot of the panel so it is still in reach after you have scrolled; a short report shows the header button only, rather than two identical buttons on one screen. Results can also be queued and attached to outgoing correspondence. New bloodwork and imaging are ordered from the **Orders** worklist (§5.17), where the outstanding order is tracked until its result comes back.

Every clinic role can read lab results, the list and the report view, and each read is audit-logged. That includes residents and clinical clerks. A secretary reads them the way they already read imaging and notes, and the pane says the view is read-only for their role: Acknowledge and filing a new report stay with physicians, nurses, and admins. If the results fail to load, the tab says they couldn't be loaded and to try again shortly rather than "No lab reports on file", and the "All reviewed" and "Select a report" prompts are hidden.

A critically abnormal value is marked on the report with a solid red badge reading **CRITICAL HIGH** or **CRITICAL LOW**, and raises a critical-urgency inbox item for the patient's physician the moment the report files. What counts as critical is read from the interpretation code on the result. EMR21 accepts the FHIR R4 codes HH and LL, which is what a real OLIS or HL7 feed sends, alongside its own `critical` string. All three now carry the same weight on screen. HH and LL used to render as pale two-letter tags no more urgent-looking than a plain H, which is backwards, since HH and LL are the codes a real feed actually sends. Hover a badge to see the raw code. If the chart has no assigned physician the item goes to whoever filed the result. Should the routing itself fail, the lab report still files and the failure is written to the log.

A result that arrives without a high or low flag now gets one from its own reference range. If the report carries a number and a range and the lab sent no interpretation, EMR21 marks it H or L against those bounds, so a result filed through the interface from a lab feed shows the same abnormal marker as one entered by hand. A flag the performing lab did send is left alone. A result with no number, no range, or a word instead of a number stays unflagged rather than being called normal.

A reference range cannot say whether a value is dangerous (a potassium of 6.9 is an emergency, a glucose of 6.9 is not), so critical is judged against per-analyte critical bands instead. When a result arrives with no flag and crosses its analyte's band, it is marked HH or LL and routed to the physician's inbox like any other critical result. The bands cover potassium (below 2.5 or above 6.0 mmol/L), sodium (below 120 or above 160), glucose (below 2.5 or above 25), hemoglobin (below 70 or above 200 g/L), platelets (below 20 or above 1000), white count (below 1.0 or above 50), creatinine (above 600 µmol/L), calcium (below 1.6 or above 3.3), bicarbonate (below 10 or above 40), and INR (above 5.0). A result reported in a unit the band does not accept is not judged against it, and an analyte with no band never becomes HH or LL. A number sent as text ("6.9") is judged on the number. The practice can tighten a band through practice settings (admin); physicians, nurses, and admins can read the bands in force. The Critical-value bands flag switches this off.

On the Labs list, a requisition with every row still pending reads "Awaiting results" instead of counting as unreviewed, and it has no Acknowledge button. A requisition cancelled or entered in error reads "Cancelled" and leaves the unreviewed count. A report filed without a collection date (some interfaces send none) plots on the trend chart on its reported or filing date, where it used to drop off the chart.

A result can also arrive as a code, for example SNOMED CT "Positive", from a feed that sends one. The word shows in the value column with a small grey badge naming the code system and the code; hover it for the full value. A FHIR export sends the coded form only when both the system and the code are present, and falls back to the text otherwise.

A lab result that is impossible on its face is refused when it is filed: a value that is not a finite number, one too large for the column, or a negative concentration. The refusal is a sentence naming the problem.

A result row can be filed without a LOINC code when the test is not in the catalogue; it is then matched by name. A report marked as resulted that holds no results at all is refused.

Once a result has been reviewed, its follow-up with the patient can be tracked as a callback. The clinician either records that the patient is aware or issues a callback with an owner. Each contact attempt is logged with who made it, when, how, and what happened; physicians, nurses, and admins can issue and close callbacks, and any clinic role can log an attempt. After three failed attempts, or 14 days with no appointment booked, the callback goes back to the provider. This runs through the API only for now; there is no screen yet, and the Result callbacks flag (§5.15) switches it off.

When a result is filed for a test that is waiting on the Orders worklist (same patient, same LOINC code, ordered in the last 90 days), the matching order item closes by itself and records which result closed it. Once every test on the requisition is back, the requisition is marked final. The Order reconciliation flag switches this off.

Each filed report also carries a one- or two-sentence summary of the panel written by the AI. The summary is given the report's own numeric bounds for every value, and it will not call an in-range result high, low, elevated, or borderline. Before this the model saw a bare number and fell back on ranges it remembered, so a hemoglobin of 141 against a range of 135–175 came back described as slightly elevated. That summary reads the values, so it runs only where the chart's AI consent has been granted. On a declined or unset chart the summary line says AI is off for this patient, and the values, units, and reference ranges are never sent to the model. Everything else about the result is unchanged.

An interpretation code EMR21 does not recognise is refused when the result is filed. The six it accepts are N, H, L, HH, LL, and critical. A result flagged H, L, HH, LL, or critical is also refused unless something stands behind the flag: a number, a word such as Positive, or a comment. A red flag with an empty value beside it tells a physician less than no flag at all.

A result carries a number or a word, never both. A report filed with 9.9 mmol/L and the word Elevated on the same result is refused at entry. An export has to send one or the other, and the number is what would be dropped. Record the value one way and put the rest in the comment. When the result is exported, the flag travels as a coded concept from the HL7 interpretation code system with the original letters kept beside it, so a consultant's system can filter on it instead of reading words.

### 5.10 Reminders (Reminders & Recalls)
The **Reminders** tab — labeled *Reminders & Recalls* on the page — lists preventive-care recalls (mammograms, Pap, diabetic reviews, immunizations due, etc.) and tasks, filterable by category (**Recalls · Preventive · Tasks**). Items are generated from guideline logic and surfaced when overdue. Work the list to keep prevention up to date.

**+ New reminder** creates one by hand. It is shown to physicians, nurses, and admins, the roles that can create a patient recall. A secretary or staff login sees **+ New task** instead, which opens a window titled New task for a personal task on their own list, with no patient attached; the save button reads **Create task**. The server refuses a patient recall from a front-desk role by name. Front desk can mark their own personal tasks done or dismissed, and on a patient recall the Done and Dismiss controls are hidden for them, since those stay with physicians, nurses, and admins. The task title (and a custom recall type) is capped at 200 characters with a live counter. A due date earlier than today shows a small amber "This date is in the past." under the field. You can still save it. After you save, the page moves to the Overdue or Upcoming list that holds the new item, so it doesn't disappear onto a tab you aren't looking at.

If the patient already has an open recall of the same type, the reminder is still created, but the window stays open to say so, naming the existing one's due date and status, so you can tidy the list if only one is wanted. The audit row records how many duplicates were open.

When a recall's status is changed through the API with a reason, the reason is added to the recall's notes as a new line such as "[dismissed] patient declined", and the audit row records that a reason was given.

A booking made against a reminder can be linked to it. While that appointment is live, the reminder counts as actioned and drops off the overdue list and count. If the appointment is cancelled or marked a no-show, the reminder goes back on the list with a note saying why. The link is made through the API for now; the booking window does not offer it yet. The Recall ↔ appointment link flag (§5.15) switches it off.

Sending a reminder is open to the front desk as well. A secretary or staff login sees **Send** on each patient recall, even though Done and Dismiss stay hidden for them. After Done, the list reloads, so the next recall of a recurring series shows straight away. Closing a recurring recall never stacks a second open copy, and reopening one clears its completed date.

A recall whose reminder has been sent stays on the outstanding list, in the stats, and on the Panel until the care is done. It carries a "Reminder sent" pill with the date, so a sent reminder is not mistaken for a completed recall. The patient picker in that window shows each match's full given names and date of birth, so two patients called Boris Larsson are told apart, and once you pick one the list stays closed. When more matches exist than fit, the list ends with "Showing 8 of 23 — keep typing to narrow" rather than cutting off silently. Picking a patient clears any "Select a patient" error.

A generated recall is dated from the patient's chart. The AI proposes a due date, but it is only accepted when it falls within a plausible window for that screening interval; otherwise the date is computed from the patient's last signed note plus the cadence for that recall type. Recalls no longer arrive already years overdue. Each generated recall also carries the patient's primary physician as its owner and a repeat interval derived from its type, so it appears on the right provider's list and comes round again on schedule. The interval is read from the recall's own wording by whole words, so an HPV vaccine series is a twelve-month vaccination cadence rather than being caught by the thirty-six-month Pap rule.

### 5.11 Panel
A roster / population-health view. A physician is always scoped to their own panel. Admin and nurse see the whole practice and can scope to a single provider, with demographic filters (sex, age range). The provider dropdown lists everyone including the clinician who is signed in. Use it to track high-risk patients and preventive-care gaps.

The page has two tiers, matching what the API serves. The panel counts and the practice summary identify nobody, and every clinic role sees them. The three views that name patients alongside their diagnoses, lab values, and risk scores, the high-risk list, the preventive-care gaps, and the cohort builder, are limited to physicians, nurses, and admins, the roles with a treatment relationship. A front-desk login sees the counts and the summary, then a card saying the patient-level views are available to the clinical roles. PHIPA's minimum-necessary rule is the reason: front-desk work needs the schedule and a chart when someone asks for it, and a disease register for the practice is more than that.

**Find a patient cohort.** The cohort builder at the top of the page searches your panel by disease or active problem, by lab analyte (optionally only abnormal results), and by free keyword across conditions, medications, and labs. Terms are combined with AND and applied inside whatever scope and demographic filters you already have set. Each result row shows the patient, age, sex, and what it matched on, and the list is sortable. Long result sets are capped, and the page tells you when that has happened.

Results can leave the page three ways. **Export CSV** downloads the cohort list itself. **Export PDFs** and **Export FHIR** (physician or admin) download a `.zip` holding one chart per patient in the cohort, either as a chart PDF or as a FHIR R4 Bundle. Batch exports are audit-logged with the patient count and IDs. The high-risk list has its own Export CSV button.

### 5.12 Correspondence (Letters) and Forms
Generate referral and consult letters from the **Letters** tab, attach labs/documents, and lock them when finalized. The **Referral Tracker** shows each referral's lifecycle stage in plain words (**Draft / Sent — awaiting reply / Completed / Revoked**) and carries a **Chase / resend — message staff** button that opens a compose-to-staff message for follow-up. Chasing a referral keeps its original send date, so the tracker still shows how long it has really been waiting; the chase is recorded as a reminder sent.

Recording a specialist's reply with no appointment date asks once: "No appointment date entered. Saving records that the specialist replied and takes this referral off the chase list. Save anyway?" The button then reads **Save without a date**. If the save fails, the row says the reply wasn't recorded and the referral is still on the chase list.

The AI draft is written from the patient's chart, not from the specialty alone. The model is given the demographics, active problems, active medications (the same CPP plus active-prescriptions list the safety checks read), and active allergies, and is told that block is the only source of clinical fact. A section with nothing in it is stated as "None recorded on the chart" so there is no silence to fill. Anything the chart does not hold comes back as a bracketed placeholder such as [investigations to be added] for you to complete. Health-card number, address, and phone are never sent. Read the draft before you sign it; it is a starting point, and the chart is the record.

The draft will also not state a clinical negative the chart does not record. It will not write that the patient has no side effects, no further allergies, or a normal examination on the strength of nothing being written down. An unrecorded finding is unknown. Where the letter needs a statement like that you get a bracketed placeholder, such as [tolerance of current medications to be confirmed], to complete yourself. The only absences it reports are the sections the chart explicitly holds as empty, and it words those as none recorded.

If the model does not answer, the letter box says "AI draft is unavailable right now. You can still write the letter manually" and leaves whatever you had already typed in place. The first request of the day can take a while because the model loads cold. On a chart without AI consent the **✦ AI Draft** button is greyed out from the start, and hovering it says AI is off for this chart and to write the letter manually.

The **Forms** tab is the clinical forms library, in two tracks:

- **Clinic-authored forms** — structured templates EMR21 renders as a letterhead PDF (e.g., Diabetes Annual Review Checklist, PHQ-9, Physician's/Sick Note, Referral Pre-Screen, Ottawa Ankle Rules). Pick a template, optionally **AI-prefill** it from the chart, complete it, and save it — the completed form is filed to the chart's correspondence.
- **Official fillable forms** — the real government/agency blank PDFs (e.g., WSIB Form 8, CRA T2201 Disability Tax Credit, ODSP, MTO, accessible-parking) filled on their official layout with this patient's chart data (name, DOB, last-4 of OHIP, address, physician, date).

The PHQ-9 scores itself as you fill it in. The form shows the total out of 27 and the severity band (Minimal 0–4, Mild 5–9, Moderate 10–14, Moderately severe 15–19, Severe 20–27) once all nine items are answered; until then it says how many are answered and that no total is calculated. The question text is the real wording rather than "Question1". A positive item 9 raises a red flag that gives the item score, for example "Item 9 is positive (2/3) — assess suicide risk." Submitting asks first, naming any unanswered items and a positive item 9. On the completed-forms list the collapsed PHQ-9 row carries the total, the band, and an item-9 badge when it applies. The scoring is plain arithmetic and never AI.

Completed and filled forms can be **printed** as a PDF and, for physicians/nurses/admins, **faxed or emailed** to a recipient. Prefill, print, and send each have a fully manual path; a patient's declined-AI status still allows source-prefill, print, and send. The **admin** curates the library from **Settings** — enabling/disabling templates, ingesting an official blank PDF (EMR21 introspects its form fields), and mapping those fields to chart data.

### 5.13 Billing
Capture OHIP billing tied to the encounter, from the **Billing** nav item or the chart's **Billing** tab. Most billing happens at the end of a visit: when you end an encounter (from the Encounters tab or via SKIPPY, §5.16), a billing confirmation drawer slides up with an AI-suggested OHIP code and rationale for you to review and confirm, so visits aren't left unbilled. The suggestion reads the visit's appointment type, so a telephone or video visit is steered to a virtual-care code rather than an in-person assessment code; when the modality is virtual the drawer flags it for you to confirm the exact code against the current Schedule of Benefits. The Billing screen is titled **OHIP Billing**; an **admin** sees practice-wide claims across all providers and can filter by provider, while a physician sees their own.

The drawer offers two ways out. **Confirm & Bill** stays disabled until an OHIP service code and a diagnostic code are both entered, with an inline hint saying why. The diagnostic code field sits directly under the service code, so it stays put while you move to it even when a code warning appears. **Close without billing** is the path for a visit that should not generate a claim, and it needs neither code. The ✕ in the drawer's corner, or Escape, puts the drawer away and leaves the visit in progress; **End & bill** brings it back.

Only physicians and admins bill. A nurse who ends a visit gets the drawer without a code field and the line "Billing is done by the physician. Close this visit without billing; they submit the claim." A resident or clinical clerk sees the same with "Billing is done by your supervising physician", and their note still goes to the preceptor for co-signature. The server refuses a fee code sent by a nurse or a learner.

The **Diagnostic code (OHIP)** field in the drawer is the 3-digit code the Ministry returns a claim without. It arrives pre-filled from the practice default (Settings, Billing section, Default diagnostic code, set by an admin) and a small "practice default" tag says so; change it when the visit's diagnosis is something else. Every claim row on the Billing screen shows its diagnostic code. A draft claim with none shows **+ Add diagnostic code (required to submit)**, and a draft's code can be edited in place. Submitting a claim with no valid diagnostic code is refused with a message that says to add it on the draft in Billing and then submit. Previously a blank code under Confirm & Bill closed the visit and wrote no claim at all, which was easy to miss at clinic speed.

The code you enter has to be one EMR21 holds. Type something the catalogue does not contain and the claim is refused before anything is written, with a message naming the code and saying that this build carries a demo subset of the Schedule of Benefits rather than the whole thing. Real codes can land there too, so the message points you at the catalogue instead of implying the code doesn't exist, and a **Show the catalogue** link in that message lists the codes and their descriptions right there in the drawer. The fee is taken from the catalogue entry, never from the number sent with the claim.

The rationale block under the code shows what SKIPPY wrote. When there is no rationale it reads "None recorded" and the link beside it says **Add**, so you can type one yourself.

If you change the code, the AI's rationale stops presenting itself as the justification for it. The block says the rationale was written for the code SKIPPY suggested rather than the one now in the button, and asks you to record why the new code applies. The text is still shown, greyed, and your own typed rationale is never touched. Before this the button could read "Confirm & Bill A007" while the sentence under it argued for A001.

A claim whose fields do not match what EMR21 expects is refused outright, naming the field it could not read. Previously a claim sent with the wrong field names came back reporting success and left an empty $0 draft sitting on the billing screen with no code and no explanation of where it came from.

The suggestion reads the visit's signed note, so it runs only on a chart whose AI consent is granted. On a declined or unset chart the request is refused with a message naming the manual path, and the note is never loaded, let alone sent to the model. Each AI read of a note is written to the audit log, recording the patient and encounter but no note text and no health-card number.

A physician whose account carries no CPSO number is treated as billing outside Ontario. They get the clinical visit summary with the billing fields left empty and the rationale reading "Not applicable — OHIP billing codes are Ontario-specific", rather than an Ontario code that cannot apply to them. The counselling-time prompt (§5.16) stays quiet for the same reason. Everything else on the Billing screen still works: the claim list, creating a claim, and typing a code by hand.

Submitting a draft claim to OHIP — a single claim or **Submit all drafts** — first asks for an **in-app confirmation**, because submission cannot be undone. The service-code catalogue holds the common Family Practice assessment codes (A001 minor, A007 intermediate, A003 general, and so on) plus the counselling code **K013** (the same code SKIPPY's encounter timer prompts for, §5.16). Codes and fees are drawn from the Ontario Schedule of Benefits.

A claim entered by hand from **New billing claim** cannot carry a service date later than today.

The **Approval rate** card at the top of the Billing screen is approved claims divided by adjudicated claims (approved plus rejected) this month, and the line under it names that count, for example "of 12 adjudicated". Before any claim has been adjudicated it shows a dash and "none adjudicated yet" instead of 0%. The card used to count drafts, so three unsent drafts read "0% of 3 submitted".

OHIP submission in this build goes to a demo adjudicator, which rejects about one claim in twenty with a random reason. Those rejections are labelled: the claim row carries an amber Simulated badge next to the reason, and hovering shows the full text, which begins "Simulated adjudication (demo, not an OHIP response)". A demo rejection reading "Duplicate claim" used to look like a real Ministry response.

### 5.14 Settings (your profile)
**Every user** can open an **Account Settings** panel from the user area at the bottom of the left nav (click your email or the gear icon) to update their display name and change their password (current password required; new password must meet complexity rules). The CPSO number field appears only for physicians and residents. Physicians and admins additionally get a full **Settings** page from the nav rail.

### 5.15 Admin Panel (admin only)
Create and manage user accounts, assign roles (Admin, Physician, Nurse, Staff, Secretary, Resident, Clinical clerk), disable departing staff, and manage practice-level settings. Admins also curate the **forms library** from Settings: enable/disable clinic-authored templates, ingest official blank PDFs (EMR21 introspects their fillable fields), and map those fields to chart data (§5.12).

The **Feature flags** section of Settings holds practice-wide switches for newer surfaces. Everything is on by default; switching one off hides that surface for every user of the practice without a deploy, and the change is written to the audit log as a configuration change naming the flag. When a switched-off surface is reached anyway, its message names the place to turn it back on, Settings → Feature flags. Sixteen flags exist today:

| Flag | What it controls |
|------|------------------|
| Guided tour launcher | The demo walkthrough pill (§4) |
| Server-side sessions | Sessions bound to the browser they were issued to; a portal sign-in ends clinician sessions on that browser (§3) |
| Audit log: background polls | Files the minute-by-minute inbox and schedule poll reads in their own log (below) |
| Error boundary | The "This page ran into a problem" panel instead of a blank page (§4) |
| Personal layout | The collapsible left menu and the default pin, saved per user (§4, §5.6) |
| Critical-value bands | HH/LL from per-analyte bands on results that arrive unflagged, routed to the physician inbox (§5.9) |
| Inbox → lab report link | The Open lab report link on a lab-result inbox item (§5.3) |
| Coded lab results | Accepting a coded result (system, code, display) and showing its code badge (§5.9) |
| Outstanding orders | The Overdue card on Orders and the Overdue orders tile on the Dashboard (§5.17) |
| Order reconciliation | A filed result closing the matching pending bloodwork order item (§5.9) |
| Result callbacks | Patient-aware and callback tracking on a reviewed result (§5.9) |
| Recall ↔ appointment link | A booking linked to a reminder taking it off the overdue list (§5.10) |
| Result escalation | Critical results to the on-call pool, and unreviewed results moving along the coverage chain (§5.3) |
| Flowsheets | The diabetes and hypertension flowsheets (§5.6) |
| Screening honesty states | The screening line on the Rx form, override, Modify, refill, Send step, and Inbox refills (§5.8) |
| Learner supervision | The learner banner, the preceptor requests, the Dashboard review card, and the preceptor review bar (§5.7). Off does not change the rule that a learner's signature waits for review |

**Front-desk access to clinical notes** sets what secretary and staff accounts can see of a patient's notes, on the Encounters tab and in the chart PDF export. The three choices are: No access (front desk cannot open clinical notes); Metadata only (note type, status, author, and dates, never the text), which is the default; and Full (front desk reads the note text, not recommended under PHIPA need-to-know). Physicians, nurses, and admins are not affected. Only an admin can change it, with **Save front-desk access**.

The Security & Privacy section's **Session timeout (min)** is enforced: a session idle for longer is signed out (§3).

Practice settings also hold the clinic hours and the appointment types. Clinic hours are listed Monday to Sunday. A day's closing time has to be after its opening time, in 24-hour HH:MM; otherwise the save is refused with "Closing time (…) must be after opening time (…). Mark the day closed instead if the clinic does not open." A day marked closed is exempt. An appointment type's length has to be 5 to 240 minutes, and a refusal names the type, for example "Follow-up: duration must be 5–240 minutes. Nothing was saved."

Three practice settings have no screen of their own yet and are set through the practice settings API by an admin: the per-analyte critical bands, the overdue limits for orders (in days, 1 to 365, per type: bloodwork, imaging, referrals), and the result-escalation rules (days before an unreviewed result moves on, the on-call pool, and each provider's coverage chain; only active physicians and nurses of the practice can be named). Each change is audit-logged.

Sessions can be ended from the server. Any clinic user, residents and clinical clerks included, can list where they are signed in, end one of those sessions, or sign out everywhere. An admin can end every session of one user in the practice, or sign the whole practice out at once. Each of these is written to the audit log with a count, never with the sessions' details. These actions are API-only for now; no screen offers them yet.

Background polling is kept out of the main audit log. The app checks the inbox and the schedule every minute while a page is open, and each of those reads used to land in the audit log as a READ, which buried the rows a human caused. Those poll reads now go to a separate log. The Audit Log section has a **Show background polls** toggle that reads that log with the same filters and columns, and Export CSV exports whichever log is showing; the export's own audit row names which one.

**The audit log can be asked a question.** The Audit Log section of the Admin panel filters by date range (**From** / **To**), by user email, by patient ID, by action, and by resource type. Results page through the server 50 at a time with a running "Showing 1–50 of …" count, so "who opened this chart on Tuesday, and what did they do" is a question you can answer on screen.

Refused requests are logged too. Every time the server turns someone away (not signed in, or signed in without the role for that action) it writes an ACCESS_DENIED row, shown in red, with the address that was refused. The IP column carries the connecting port where there is one. The audit table itself only accepts new rows: the database refuses any change to or deletion of an existing one. **Export CSV** downloads whatever the current filter holds, up to 50,000 rows.

The patient ID filter finds every entry that touched that patient. That includes the chart itself and anything that belongs to it: prescriptions, claims, note signing, ending an encounter, inbox triage, worklist actions, labs, imaging, referrals, recalls, documents, and immunizations. Before this change most of a clinic day's writes on a chart were missing from that filter because they were logged against the child record's own ID. The match is made when you run the search, so it covers the whole history, not only entries written from now on.

Beside the filters, **Who accessed this patient** answers the privacy officer's usual first question in one step. Put a chart ID in the Patient ID filter and the button becomes active. The report covers the From and To dates you set, or the last 30 days if you set none, and lists one row per user with their role, first seen, last seen, a count of actions, and the record types they touched. It shows who and when, never what was in the record. Running it is itself written to the audit log. On a very busy chart the per-action breakdown is capped at 5,000 groups and says so; the user list and the totals stay exact.

Three things about the export are worth knowing. It is admin-only. It is itself written into the audit log, with the filter recorded, before the file is produced, because taking a copy of the access trail is the kind of act the trail exists to record. And it carries access metadata only: timestamp, user email and role, action, resource type and ID, and IP address. The old and new values never leave, so no health-card number can ride out in a spreadsheet.

### 5.16 SKIPPY — the AI assistant
SKIPPY is the built-in clinical assistant. Open it with **⌘K / Ctrl+K** or the **SKIPPY** launcher at the top-left of the header. On desktop it docks as a panel on the right of the screen (it shares that space with a pinned chart tab — opening SKIPPY temporarily hides a pinned panel and vice-versa); on mobile it's a floating button that opens a bottom sheet.

**Ask anything, any time.** Type a clinical or workflow question and SKIPPY answers, aware of context: the patient you're seeing, how long the current encounter has run, how many patients have arrived, and your unread inbox count. General (no-patient) questions are always available; questions about a specific chart require that patient's AI consent (see "Consent gate" below).

A question about a patient is answered from that patient's record, read at the moment you ask. The scope is the chart you have open; if no chart is open it falls back to the patient whose encounter is active. An empty section of the chart is stated as empty rather than filled in, so "no allergies recorded" means the list was read and was empty. Medications the chart documents from elsewhere are kept separate from prescriptions written here, so an outside medication is never reported as one this practice prescribed. Loading the chart to answer is itself a clinical access and is audit-logged alongside the question.

What SKIPPY reads includes the patient's preventive care and their immunization record. Preventive-care items are marked overdue by the same date arithmetic the chart's Preventive Care panel uses, so the two agree. Ask what is due and you get every overdue item back. SKIPPY used to answer that question without the preventive-care data in front of it, and would say nothing further was noted as overdue while the panel on the screen behind it read overdue influenza and overdue colorectal.

#### SKIPPY across the encounter — start, during, and finish

![SKIPPY across an encounter — start (reminder + timer), during (counselling-time prompt, note-signed ✓), finish (audit-window summary + billing drawer), plus post-sign chart suggestions and the consent gate.](images/05_skippy_encounter.svg)

*Figure 5 — What SKIPPY does as an encounter starts, runs, and finishes.*

![Screenshot — SKIPPY open as the docked right rail, showing the encounter-start reminder.](images/screen_4_skippy.png)

*Screenshot — SKIPPY docked on the right (the page shrinks rather than being covered). It opens with the **start-encounter reminder** (noting K013/K014/K017 billing), the next-patient chip with the name withheld, unread messages, and the **Ask SKIPPY** box.*

SKIPPY is woven through the encounter workflow (§5.7) at three points:

- **Starting an encounter.** When you open a chart with no visit active, SKIPPY shows a **reminder banner** to start the encounter, and you can start it right there. The moment an encounter begins (from the Encounters tab, the schedule, or the reminder), SKIPPY opens an **encounter timer** — a live status chip showing the patient's first name and elapsed time.
- **During the encounter.** The timer keeps running. Once the visit reaches counselling length, SKIPPY surfaces the counselling code so a long visit isn't under-billed. Counselling is K013, billed per half-hour unit (a unit is 30 minutes or the major part of it, so roughly 1 unit at 15 minutes, 2 at 45, 3 at 75; beyond three units in a year it becomes K033). It is only appropriate when counselling was the primary service, so the physician decides whether it applies. A physician whose account has no CPSO number does not get this prompt at all, since OHIP codes are Ontario-specific (§5.13). When you sign the note, the chip shows a green ✓ ("note signed"). Throughout, you can ask SKIPPY context-aware clinical questions about the patient in front of you.
- **Finishing the encounter.** Selecting **End encounter & bill** sends SKIPPY to read the *audit-log actions* recorded during the visit window (note signed, labs reviewed, scripts written, etc. — resource types only, never patient identifiers) and draft a **visit summary plus a suggested OHIP code and rationale**. The summary is checked against the action count shown on the same screen: if it claims nothing was recorded while rows exist, it is replaced with a sentence built from those rows, grouped by what was touched and in the order you worked. That opens the **billing confirmation drawer** along the bottom of the screen; you review, adjust if needed, and confirm, and only then is the encounter closed and the claim created. This also works on any **past** visit via "Bill this visit." You can end a visit from SKIPPY on any page, not only from the chart, and the drawer opens either way. If the AI summary can't be produced, the drawer opens in manual-entry mode rather than closing the visit silently.

#### Post-encounter chart suggestions
After you **sign** a note, SKIPPY reads it and proposes the structured chart updates you'd otherwise hand-enter — new problems, medications, family history, social history — as individual **Accept / Modify / Dismiss** cards shown beneath the note. This is the most safety-critical AI feature, so guardrails are enforced in code, not just by prompting: every suggestion must quote a **verbatim snippet of your note** (no hallucinated findings), extraction runs on the **local** model only (PHI never leaves the box), suggestions are de-duplicated against the existing chart, negated/uncertain findings are filtered out, the list is capped, **nothing is ever auto-applied**, and every Accept/Modify/Dismiss decision is logged. The cards sit in a scrolling block under the note with a collapse toggle on the header, so a long list never pushes the note off screen. A medication suggestion is matched to the chart on the drug name rather than the whole label, so "Ramipril 10 mg tablet" already on the list suppresses a suggestion of "ramipril" from the note. The three spellings a drug can carry, the catalogue label on a prescription, the bare name on a documented medication, and whatever the note says, no longer read as three different drugs. If a card is accepted and the server refuses the write because the patient is already on that drug, the card says "already on the chart — not added again". It does not report a row it never created as added. The quote also has to support the suggestion. A verbatim sentence about how often the migraines come is no evidence for medication overuse headache, so on a proposed problem EMR21 checks that the citation speaks to the diagnosis it is offered for. Where it doesn't, EMR21 looks through the note for a sentence that does, usually the assessment line, and shows that one instead. A problem with nothing in the note behind it is dropped rather than shown under a plausible-looking quote you would have to read twice to catch. The check runs on problems, where the stakes are highest.

Matching a quote to a diagnosis ignores the words that describe the shape of a problem rather than name it: exacerbation, chronic, stage, disease, type. Those words are shared by illnesses with nothing to do with each other, so a note entirely about a patient's eczema could produce a suggested "Asthma exacerbation" citing the sentence about the eczema flaring, on the strength of the one word they had in common. The illness itself now has to appear in the quote. A one- or two-word diagnosis needs every identifying word, so "Atrial fibrillation" is not supported by a sentence that says only "fibrillation", which could as easily be the ventricular kind. A quote with real evidence in it still works: "Asthma flare since Monday" still supports "Asthma exacerbation".

A sentence ruling something out is not offered as proof of it. Alongside denies, no, not, without and negative for, EMR21 reads absent, absence of, unremarkable, free of and ruled out as denials, which are the phrasings a clinician actually writes in the objective section. A note reading "Psychotic features were absent throughout" used to produce a suggested problem of severe depression with psychotic features, citing that sentence. One knock-on is worth knowing about: a finding that is itself an absence, such as absent pedal pulses, will not support a suggestion either, so that one has to be added by hand.

Vitals already cite the reading they came from. If the model returned no blood-pressure vital at all, EMR21 scans the signed note itself for a reading written as "BP 128/76" or "128 over 76" and adds that one suggestion, which goes through the same verbatim, negation, and duplicate checks as every other card and still needs your Accept. A suggested problem arrives unconfirmed. Verification status is a statement about what the clinician believes, so the card carries an unconfirmed badge until you accept it, and Modify is what raises it. The ICD-10-CA code on a suggested problem is looked up in the same curated code list the manual problem typeahead uses. Where the wording matches more than one code it stays uncoded instead of being guessed at, so "type 2 diabetes mellitus" (which prefixes both E11.9 and E11.65) reaches you without a code rather than with the wrong one. Problems already on the chart suppress a suggestion by the same word-boundary match used for medications, so the note's bare "atrial fibrillation" collapses onto a coded "Atrial fibrillation, unspecified" already listed. Matching reads the identifying words in any order and sets aside words that say how a problem is going rather than what it is: stable, compensated, controlled, unspecified, ongoing, established, known, longstanding, baseline, current. So "Stable atrial fibrillation" is recognised as the atrial fibrillation already on the list, even though the extra word sits at the front. Words that genuinely change the picture are left alone, including uncontrolled, decompensated, and a different stage number. That suppression runs one way only. A suggestion that says more than the chart does still reaches you, so an existing "Asthma" does not hide a proposed "Asthma exacerbation", and a problem the chart records as resolved or inactive does not suppress a live one. When SKIPPY reads the note and finds nothing new to add, it says so ("SKIPPY reviewed the note — no new chart updates to suggest") rather than leaving a silent blank, so you know it ran. If the AI is unavailable you simply get no cards, and it never blocks closing the encounter.

#### Personality & privacy
- **Personality modes** — a selector switches SKIPPY's tone: 🩺 Professional, ⚡ Skippy, 😎 Chill, or 🌸 Haiku. This changes wording only (greetings, billing nudges) — never clinical behaviour or codes.
- **Privacy** — while you're inside a chart, SKIPPY's status chips show only how many patients have arrived, never the next patient's name. The chip says why it is blank, reading "Name hidden while you're in a chart", rather than sitting empty under a header that already says the next patient is upcoming. The sign-in greeting at the top of the panel follows the same rule. It is written once when you log in, and while you are inside a chart it refers to the first patient of the day by role instead of by name, so the greeting no longer undoes the suppression in the chip two lines below it. Every personality voice is covered, including the professional one, which used the patient's full name.

#### Consent gate (AI is additive, never required)
Every patient chart carries a master **AI consent** status. If a patient has not granted AI consent, every AI surface on that chart is switched off while **100% of the EMR remains usable manually**. That covers the SKIPPY rail, the scribe, post-encounter chart suggestions, the billing code suggestion, prescription suggestions from a note, the AI summary on a filed lab report, the referral-letter draft, the inbox reply draft, and the written rationale on a schedule slot. Buttons that would be refused are greyed out or hidden before you press them, with the reason on hover: the letter's AI Draft, the inbox reply's AI draft, and Start + AI Scribe, which is not shown at all. Granted means AI assistance is available. No feature is AI-only; SKIPPY only makes the manual workflow faster and safer. On a phone the same state shows on the floating SKIPPY button as an "AI off" tag, and the Ask box in the bottom sheet is disabled (pressing Enter does nothing), so the mobile view matches what the desktop header says about that chart.

This applies to billing too. If you end an encounter on a chart that has declined AI, the billing drawer opens in manual-entry mode and you type the OHIP code yourself. The drawer never dead-ends when the AI suggestion is unavailable; it falls back to the manual path.

Where EMR21 already knows this chart's consent is off, the drawer opens straight into manual entry without asking the model at all. You don't wait on a request whose answer is settled, and a chart with AI switched off generates no AI traffic. If the visit was ended from the Orders worklist or from another patient's chart, where the consent on hand may belong to someone else, EMR21 asks and the server's refusal brings you to the same manual drawer.

The drawer also distinguishes the two reasons it has no suggestion for you. On a chart where AI consent was declined it reads "AI is off for this patient — enter the OHIP billing code below", which matches the footer's "Manual entry — no AI used". It says the summary is unavailable only when something actually failed. Manual entry is the full path here, not a degraded one, so a consent-off chart should not read like a broken system.

### 5.17 Orders — the outstanding-orders worklist
The **Orders** nav item opens a page titled *Outstanding orders*. It is the tracking board for everything sent out of the clinic that is still waiting on something to come back: referrals awaiting a reply, bloodwork awaiting results, and imaging awaiting a report. Every clinic role can view the worklist; placing and actioning orders is limited to physicians, nurses, and admins.

Cards across the top count the board by status: **Results back**, **Chase due**, **Awaiting** (sent, nothing back yet), and **Actioned**, plus a red **Overdue** card. Click one to filter to it, and use the type dropdown to narrow to referrals, bloodwork, or imaging. A line under the cards says which list you are looking at.

Overdue lists orders still waiting past the practice's limit for their type, most overdue first. The defaults are 14 days for bloodwork and 30 days for imaging and referrals, and the line under the cards names the limits in force. Each row says how long it has waited and how far past the limit it is, for example "20 d waiting · 6 d past 14 d". The Dashboard's Overdue orders tile opens this list, and so does the address `/worklist?view=overdue`. When the practice has overdue tracking switched off, the card is hidden and a line says "Overdue tracking is off for this practice"; the other counts are unaffected. Two buttons at the top right place new orders: **+ Order bloodwork** (pick a patient, choose from common panels like CBC, HbA1c, lipids, Urine ACR, or NT-proBNP, or type a test that isn't listed into the Other (specify) line, then write the clinical indication in a two-line box you can read back, the same shape as the imaging requisition beside it) and **+ Order imaging** (pick the modality: X-ray, ultrasound, CT, MRI, echo, mammogram, DEXA, then the body region and the indication). If you have an encounter open, both modals arrive with that patient already selected and their date of birth on the chip, so ordering for the patient in the room doesn't mean searching for them again. A change control is always there if the order is for someone else. A bloodwork order can carry several tests; each test tracks its own result. An imaging order has to name one of the modalities in that list; a blank or a typed-in modality is refused by the server. Placing an order is written to the audit log as a CREATE on the order itself (an imaging order or a lab order), so an audit search for what was created on a chart finds it.

An imaging order needs both a body region and a clinical indication, because the facility can't perform the study without them. Both fields are marked required, and until both are filled in the modal says "Add a body region and a clinical indication — the imaging facility needs both." The server refuses a blank one too. If the order fails to save, the modal says it wasn't placed and to try again.

Placing an order does not just close the dialog. Both modals finish on a requisition step. The order is confirmed onto the worklist and you get a printable requisition PDF, with the health-card number masked, to give the patient for the lab or imaging facility or to file to the chart.

When a result or reply arrives, mark the order **Result in** to reconcile it, either a whole order at once or a single test inside a multi-test bloodwork order. Rows whose results are back open by themselves and show the returned values in place: each test with its value, its reference range, and an abnormal flag, red for high and amber for low. From that expanded row you can acknowledge the result, use **Message staff** to send a triaged task to the Staff Inbox, or open the patient's chart, without leaving the worklist. Acknowledging a bloodwork order that still has tests pending warns you first and tells you how many. Orders you no longer need to chase can be dismissed. A bloodwork or imaging order that is no longer wanted can be cancelled with **Cancel order** while nothing has come back on it. The modal asks for a reason, the order stays on the worklist marked Cancelled for 30 days, and the cancellation and the reason go to the audit log. Once a result is in, cancelling is refused. If the lab has already drawn the sample, or the patient has booked with the imaging facility, cancel with them too.

A bloodwork order where some tests are back and others are still outstanding offers **Withdraw remaining (n)** instead. It asks for a reason, keeps the results that are in, marks the outstanding tests withdrawn (struck through in the row, with the reason in the audit log), and closes the requisition. Physicians, nurses, and admins can do this.

The same order windows open from the chart too: **+ Order bloodwork** on the Labs tab, **+ Order imaging** on the Imaging tab, and both on the encounter bar while a visit is open (§5.7). From a chart tab the patient chip says "From this chart" rather than "From the open encounter".

A double click on the place-order button places one order. The server also ignores an identical bloodwork or imaging order for the same patient placed within two minutes of the first.

A lab result filed for a pending test closes that test's order item by itself (§5.9), so a row can move to Results back without anyone pressing Result in. Everything else on this board is manual, and nothing here is AI-assisted.

### 5.18 Import — bringing in charts from another EMR (admin only)
The **Import** nav item lets an admin migrate patient charts from another Ontario EMR. It reads an OntarioMD CDS export — one `.xml` per patient, or a `.zip` of many — from PS Suite, OSCAR, TELUS CHR, or Accuro. Choose the file and select **Preview** for a dry run that writes nothing: you see how many files are importable, how many look like possible duplicates of patients already in EMR21, and how many could not be read. Set what to do on a duplicate (skip, or import anyway as a new record), then commit the import. If anything fails, no half-imported patients are left behind. Import is an admin practice-migration task and uses no AI.

---

## 6. The Patient Portal (for patients)

Patients log in separately at `/portal/login` and see only three areas:

- **My Health** — a read-only summary of their own clinical information.
- **Requests** — send a request or message to the practice; these land in the clinic Inbox. The subject is capped at 120 characters and the details at 5,000, each with a live counter, and the patient's recent requests appear under the form with their status pills. If the clinic's server refuses a request, the patient reads its reason rather than a bare "try again". A request from a patient who has no primary physician yet goes to the practice's front desk (a secretary, else staff, else an admin), and the patient can still see it and read the reply. Pressing Submit twice, or sending the same request again within a minute, files it once; a request with the same subject but different details counts as new.
- **Intake** — complete intake/booking forms before an appointment. On the emailed new-patient intake form, each answer is capped at 5,000 characters, with a counter under the box.

**My Requests**, on the My Health page, lists the patient's own refill, referral, sick-note, and message requests with a status of submitted, in review, or done. A medication with an open refill request keeps its "Requested ✓" mark across reloads. When the clinic replies to a request from the Inbox, the reply appears under that request, headed "Reply from" the clinician who sent it, and the clinic gets a read receipt.

Patients can book appointments through the portal; the booking flows into the clinic **Schedule**. On **My Health**, the upcoming-appointments list shows only genuinely upcoming bookings; completed, cancelled, and no-show visits appear under **Past Visits** instead. Patients cannot see other patients' data or any internal clinic screens.

Past Visits is built from finished encounters as well as completed appointments. A walk-in, a phone visit, or anything else started straight from the chart is a visit the patient can see, where before only a booked slot counted and a chart holding eight encounters could show "No past visits on record". Where a finished encounter and a completed appointment fall on the same day they are one visit, and the encounter is what the patient sees. Each row carries the date, a plain visit-type label (Office visit, Virtual visit, and so on) worked out from the encounter's FHIR class code, and the status. The clinician's own reason for visit stays on the clinic side; the reason a patient typed when booking is still their own words and is still shown.

The email address is matched without regard to capitals or stray spaces, so "Jane.Doe@Example.com " signs in the same as the address on file, and the portal header shows the address as it was matched. Portal sign-in is protected against guessing: after 5 failed attempts the email is locked out for about 15 minutes before it can try again. A wrong email or date of birth is reported on the sign-in form ("Sign-in failed. Please check your email and date of birth."). A date of birth before 1900 or after today is refused with an in-app message rather than a browser bubble.

The demo link that opens the portal as a sample patient (`/portal/login?demo=1`) works only where the server allows it, and never on a production installation. Elsewhere it shows the normal sign-in form with "The demo portal is not available on this deployment. Please sign in below." If the server does not answer within 8 seconds the sign-in form appears instead of a page stuck on "Opening the patient portal".

Sometimes one email and date of birth belong to more than one chart, as with twins registered under a parent's email. The portal does not guess between them. It says so and asks for the patient's first name, then signs in only if that narrows it to exactly one chart. The lockout rules are the same.

The pre-visit questionnaire will not take a completely blank submission. Submit questionnaire stays disabled until at least one field has something in it, with a line underneath telling the patient that "no change" is a fine answer. The server refuses a blank questionnaire too, so the clinic's intake queue no longer collects empty rows that a clerk cannot tell apart from a patient who had nothing to report.

---

## 7. Privacy and Audit (what every user should know)

- Every access to clinical data is logged: who did it, what they did, which record, from which IP address, and when. The audit trail is append-only, enforced by the database itself, and exists to meet Ontario's PHIPA access-reporting rules. Assume your access is recorded.
- An attempt the system refuses is logged as well (ACCESS_DENIED), so trying a page or action your role does not allow leaves a record.
- Patient reads and writes record the acting user's email and role, not only an internal account id, so a filtered log answers "who opened this chart" by name without a second lookup.
- An admin can query the log by date, user, patient, action, and resource type, and export the filtered result as CSV. The export is admin-only, is recorded in the log itself, and carries access metadata only, never the old or new values (§5.15). For one chart, the **Who accessed this patient** report lists every user who touched it in a date window, with first and last access and what kind of records they opened.
- The log records which fields were written, not what was written in them. Names, dates of birth, addresses, phone numbers, email addresses, and health-card numbers are replaced with a marker in the audit row, because audit records are read by people who are not entitled to the chart itself. Operational fields such as the assigned physician pass through unchanged, so the log still answers what a user changed.
- Only open charts you have a clinical or administrative reason to open.
- OHIP and health-card numbers are encrypted at rest and masked in logs. You will never see them in audit output.
- No real patient data is used in development or demos. Every demo identifier is clearly synthetic.

---

## 8. Quick Tips

- Press ⌘K or Ctrl+K to open SKIPPY, the AI assistant, and ask a question or work the encounter.
- Drag a file onto an open chart to file it to Docs instantly.
- Pin your most-used chart tab as your default so it docks beside every chart, and drag the pinned panel's divider to resize it.
- Click the EMR21 wordmark to jump back to the Dashboard from anywhere.
- Press `[` to fold the left menu down to icons, and again to bring it back.
- The **Help** link in the header opens this manual inside the app.
- Check the clinical banner (allergies, meds, problems) before prescribing.
- Sign notes promptly. A draft is not part of the legal record until it is signed.

---

## 9. Complete Feature Catalog (by category)

This is the full list of what EMR21 can do today, grouped by category. The **Roles** column shows who can perform the action (P = Physician, N = Nurse, S = Secretary, A = Admin, R = Resident, C = Clinical clerk, ▢ = Portal patient). "View" access to clinical data is available to P/N/S/A unless a row says otherwise, and is always audit-logged; the roles shown are for the *action* described. Two reads are narrower than the rest: a patient's prescriptions (§9.8) and the Panel lists that name patients (§9.14). The learner roles (R, C) are narrower still: a row includes them only where it names them.

### 9.1 Accounts, Access & Security
| Feature | Roles |
|---------|-------|
| Log in with email + password (JWT session, 60-min token, silent refresh) | P · N · S · A · R · C |
| Server-side session tied to the browser it was issued to (up to 7 days); a refresh from another browser is refused | — (automatic) |
| A patient portal sign-in on the same browser ends any clinician session there | — (automatic) |
| List own sessions, end one, or sign out everywhere (API only; audit-logged with a count) | P · N · S · A · R · C |
| Idle sessions are signed out after the practice's Session timeout (default 60 min, 5–480), with "Signed out after a period of inactivity" | — (automatic) |
| First-run setup at `/setup` on an installation with no accounts: practice name, optional group billing number, first administrator, AI setting (No AI / Local AI); closed once any account exists | — (first visitor) |
| End every session of one user in the practice, or sign the whole practice out (API only; audit-logged) | A |
| Collapse the left menu to icons (panel button or `[`), saved to your account | P · N · S · A |
| A page that fails to draw shows "This page ran into a problem" with Try again / Go to dashboard; the menu and header stay | — (automatic) |
| Account lockout after 5 failed attempts in 15 min | — (automatic) |
| View/update own profile (display name; the CPSO number field is shown to physicians and residents only) | P · N · S · A |
| Change own password (complexity-enforced) | P · N · S · A |
| Log out (clears session + per-patient view prefs) | P · N · S · A |
| Open the in-app **Help** (this manual) from the header | P · N · S · A |
| A page closed to your role says so, naming the address and the role required (the API refuses it independently) | — (automatic) |
| A nav item a role would be refused at is not offered to that role; the route guard and the menu read the same rule | — (automatic) |
| A restricted region inside a page (a chart tab, for example) carries the same card, so withheld information never renders as an empty result | — (automatic) |
| A refused save reports the server's own reason, including a field-level validation refusal, rather than a blank screen or a generic "try again" | — (automatic) |
| Create / edit / deactivate user accounts; assign roles (the picker lists Admin, Physician, Nurse, Staff, Secretary, Resident, and Clinical clerk) | A |
| Reset another user's password | A |
| View the system audit log | A |
| **Query the audit log** — filter by date range, user email, patient ID, action, and resource type, paged server-side | A |
| The patient ID filter also finds entries logged against the patient's child records (prescriptions, claims, notes, encounters, inbox, worklist, labs, imaging, referrals, recalls, documents, immunizations), across the whole history | — (automatic) |
| Every refused request (401 / 403) is written as an **ACCESS_DENIED** row naming the refused address; the IP column shows the connecting port where known | — (automatic) |
| **Export the filtered audit log as CSV** (≤ 50,000 rows; access metadata only, no old/new values; the export is itself audit-logged with its filter) | A |
| **Who accessed this patient**: per-user report for one chart over the date filters or the last 30 days (role, first and last seen, actions, record types; no record contents; breakdown capped at 5,000 groups with a notice; each run audit-logged) | A |

### 9.2 Scheduling & Appointments
| Feature | Roles |
|---------|-------|
| View today's clinic / day schedule | P · N · S · A |
| Book a standard appointment (the booking goes on the day the schedule is showing; the Date field says so) | P · N · S · A |
| Book an **urgent** appointment | P · N · S · A |
| **AI slot suggestion** (suggest best open slot; slots always offered, the model-written rationale is gated by the chart's AI consent, and the panel says whether the model or the template wrote it) | P · N · S · A |
| Reschedule / update an appointment | P · N · S · A |
| Cancel an appointment (confirm dialog names the patient and time; focus on Keep; an In Progress appointment with no open encounter offers Cancel too) | P · N · S · A |
| Mark **no-show** | P · N · S · A |
| Mark **Arrived** and **Complete** (visit lifecycle) | P · N · S · A |
| **Start Encounter** on an arrived appointment (opens/reuses today's encounter; the front desk sees "the clinician starts the encounter" instead) | P · N · A |
| Booking window: selected-patient chip shows DOB and age (same-name guard) | — (automatic) |
| **Double-booking advisory**: when the provider already has a live appointment overlapping the slot, an "Already booked … Book anyway?" notice appears as the time is chosen, and the booking window names the appointment and offers Book anyway / Pick another time (overbooking stays permitted) | — (automatic) |
| The same patient booked twice with the same provider at the same start is refused; a double click books once | — (automatic) |
| Picking a patient fills an empty provider field with the patient's primary physician | — (automatic) |
| Booking window reads the practice's clinic hours for that weekday; a slot outside hours, on a closed day, or running past midnight asks **Book anyway?** before saving (never refused); a visit that ends after closing says so under the time | P · N · S · A |
| The schedule day and slot suggestions use Ontario midnights, so an evening appointment stays on its own day | — (automatic) |
| **Practice-wide day view** with provider labels + provider filter (physician defaults to own day) | N · S · A (P via filter) |
| The provider filter lists every provider including the signed-in clinician, so a physician can filter the day down to themselves | — (automatic) |
| The mobile day list names the provider on each row when the visible day mixes providers, matching the desktop card; a single-provider day stays unlabelled on both | — (automatic) |
| Overlapping appointments split into columns; where a column is too narrow to hold both, the status pill gives way to the coloured dot and the status stays available on hover and to a screen reader | — (automatic) |
| View a patient's appointment history (Upcoming vs Past) | P · N · S · A |

### 9.3 Inbox & Triage
| Feature | Roles |
|---------|-------|
| Physician clinical inbox with **AI urgency triage** | P · A |
| Staff Inbox queue | N · S · A (· P) |
| Mark item reviewed | P · N · S · A |
| Assign an item to a user (**Assign to a clinician** picker of physicians and nurses; the assignee must be an active user in your practice) | P · N · S · A |
| Inbox page title reads **Physician Inbox** for a physician and **My Inbox** for every other role | — (automatic) |
| A completed intake arrives as a review item carrying the patient's answers, read-only and marked "NOT in the chart yet — confirm manually"; with no primary physician it goes to whoever sent the link, flagged "no physician assigned" | — (automatic) |
| Re-triage / set urgency | P · N · S · A |
| **Send an item to the Staff Inbox** (triage action + instruction for staff) | P · N · S · A |
| Mark staff-done | P · N · S · A |
| Complete a **pharmacy refill request inline** (multi-med select, inline dose/frequency/quantity/repeats editor, med-not-on-file warning) | P · A |
| The medication the request actually named is badged **Requested** and sorted to the top of the refill list; it is pre-checked only when it matches exactly one active prescription; nothing is filtered out | — (automatic) |
| **Approve & tell the patient** on a portal refill request: sends the refills, replies in the patient's portal naming them, and files the request; a failed step is named | P · A |
| Record which script a refill request refilled; a reopened request shows "✓ Refilled" with time and user and disables that row | P · A |
| See what a refill request already refilled | P · N · S · A |
| **Open lab report →** on a lab-result item opens that report highlighted on the chart's Labs tab | — (automatic) |
| Triaged tasks routed from a physician render above referral tracking in the Staff Inbox | — (automatic) |
| **Send to Staff Inbox** is pinned to the bottom of the triage panel at tablet width and up, so revealing the notes field cannot push it below the fold | — (automatic) |
| On a phone that pin is off and the button sits in normal flow, with the panel scrolling to it once a decision is picked; the pinned bar was painted over the triage options at the fold, so a tap aimed at an option could hit Send and route the item | — (automatic) |
| Refill warnings render in full (allergen / reaction / criticality; drug pair / severity / description), with the override only beside a shown warning; a field refusal lands on that medication's row and is not overridable | P · A (automatic) |
| Opening the chart from a refill request lands on the **Rx** tab (as a lab result lands on **Labs**) rather than on the last tab you used | — (automatic) |
| **AI draft reply** to an inbox item (consent-gated on a patient-linked item) | P · N · S · A |
| The draft treats the sender's message as quoted data (instructions inside it are never followed, in English or French); a draft that obeyed one is withheld, a draft claiming something was already done is flagged, and bracketed placeholders are scrubbed | — (automatic) |
| A draft that promises a future action in your name is flagged ("This draft promises something on your behalf"), advisory only | — (automatic) |
| **Reply to patient** on a portal-originated item (stored against the request, shown in the portal with a read receipt; audit-logged without the text; optional consent-gated AI draft, greyed out up front on a chart without AI consent) | P · N · A |
| View the replies already sent to the patient on an item | P · N · S · A |
| Staff Inbox labels the routed note **Triage instruction** | — (automatic) |
| Closing a **critical** item with No F/U needs an inline confirmation (optional reason kept as the triage note; the audit row records that one was given); the server refuses it without the confirmation | P · N · S · A |
| Intake review items are labelled **Intake**; the category filter offers Intake and Document | — (automatic) |
| The list-row patient chip opens the same chart tab as the detail pane (Labs for a lab result, Rx for a refill) | — (automatic) |
| **Result escalation**: a critical result also goes to the practice's on-call pool | — (automatic) |
| Run the escalation sweep: a result unreviewed after N days (default 3, 1–30) moves to the next clinician in the provider's coverage chain, each hop audited (API only) | P · A |
| Read the result-escalation rules and the clinicians who can be named in them (API only) | P · N · A |
| Set the on-call pool, coverage chains, and unreviewed-days limit (practice settings API; physicians and nurses only; audit-logged) | A |
| Inbox refill rows show the screening line (complete / partial / not screened), including on a refill that raised no warning; the confirmation after approving lists each sent script with its own screening line | P · A (automatic) |
| A preceptor request from a learner arrives in the named physician's inbox | — (automatic) |

### 9.4 Internal & Patient Messaging
| Feature | Roles |
|---------|-------|
| Send internal message (with threading) | P · N · S · A |
| View message inbox / sent / threads | P · N · S · A |
| Unread-count badge (polls each minute) | P · N · S · A |
| View messages linked to a patient | P · N · S · A |
| Resource chips **deep-link to the chart** (lab / Rx highlighted; toast if the target is gone). An Rx chip naming a drug the chart holds more than once lands on the active script, not a cancelled one | P · N · S · A |
| Mark message read | P · N · S · A |

### 9.5 Patient Records & Demographics
| Feature | Roles |
|---------|-------|
| Search / list patients (accent-insensitive: "Cecile" matches "Cécile"; apostrophes, hyphens, and spaces ignored: "obrien" finds O'Brien; a no-match search says so) | P · N · S · A · R · C |
| Invisible characters (zero-width space, BOM, word joiner, soft hyphen) are stripped from patient names on save | — (automatic) |
| **New-patient intake form** card on the Forms tab: email a new single-use link (stops any open one), see each link's state, **Revoke link** on an open one (audit-logged); completing one link closes the others | P · N · S · A |
| Open a full patient chart | P · N · S · A · R · C (header and patient record) |
| Register a new patient | P · N · S · A |
| Edit demographics from the chart's **Edit info** window (name, preferred name, DOB, sex at birth, phone, email, health card + version code; the card number can be left blank to keep the one on file) | P · N · A |
| A birth date in the future, or more than 130 years ago, is refused on create and on edit; the server enforces it independently of the form | — (automatic) |
| The primary-physician picker on registration includes the signed-in physician, so a physician can be their own patient's primary | — (automatic) |
| Registration refuses to save when "Email the new-patient intake form to this patient" is ticked with no email address, instead of creating the patient and sending nothing | — (automatic) |
| Set / edit **preferred pharmacy** (prefills the Rx send/fax modal) | P · N · A |
| Registration reports an intake-invite failure under the form (the patient is still saved) instead of swallowing it | — (automatic) |
| Archive / unarchive a patient (the header **…** menu, with an in-app confirm) | P · A |
| Starting a visit on an archived / inactive / deceased chart is refused with the status named until "Start an encounter anyway?" is confirmed; the confirmed start gets its own audit row | — (automatic) |
| Mark a patient deceased | P |
| Ontario health card number checked on create and edit (10 digits, check digit, 2-letter version code; blank for the uninsured), in the form and on the server; a refusal never echoes the number | — (automatic) |
| Chart header age in days under 1 month and months under 2 years; a long name is truncated with the full name on hover | — (automatic) |
| A phone number must be dialable (10 digits, area code not starting 0 or 1 and not an N11 service code, optional extension), on registration, in Edit info, and on the server | — (automatic) |
| A birth date giving an age over 120 is asked about once on registration (press Create patient & open chart again to keep it) | — (automatic) |
| The pinned side panel hides while its own tab is the one open | — (automatic) |

### 9.6 Cumulative Patient Profile (CPP)
| Feature | Roles |
|---------|-------|
| View CPP summary (problems, meds, allergies, history, prevention) | P · N · S · A |
| Summary med panel separates **prescribed** from **documented · not prescribed** | — (automatic) |
| Add / edit / remove **problems (conditions)** | P · N · A |
| Search **ICD-10** codes when adding a problem | P · N · S · A |
| Add / edit / remove **allergies** | P · N · A |
| Allergies carry a FHIR R4 category (food / medication / environment / biologic); an allergen typed in by name is stored as plain text rather than as a coded concept with no code | — (automatic) |
| An empty allergy list reads **Allergies not recorded** (banner, Allergies card, PDF export), never "No known allergies" | — (automatic) |
| A reaction that implies high criticality (anaphylaxis, angioedema, SJS/TEN, DRESS, airway compromise) pre-selects **High** with an editable hint | — (automatic) |
| A new allergy is checked against active prescriptions; a collision names the drug and offers **Hold** / **Discontinue** / **Leave as is**, each audited | P · N · A (warning) / P · A (hold, discontinue) |
| Remove an allergy as **No longer allergic** (kept as resolved) or **Entered in error** (never screened or summarised again), with an optional reason kept on the allergy (the audit row records only that one was given) | P · N · A |
| An entered-in-error allergy cannot be set back to active on its own | — (automatic) |
| Family-history age at onset bounded 0–120 | — (automatic) |
| Saving or retracting a vital refreshes the Summary's Recent Vitals card, pinned or not | — (automatic) |
| **Flowsheets** (diabetes, hypertension): latest value per element from labs, vitals, and hand entries against guideline targets and intervals; view and add or remove hand entries (API only) | P · N · A |
| Set or remove a patient's individualised flowsheet target, with a reason; the old target is superseded, never deleted (API only) | P · A |
| List the flowsheet definitions (API only) | P · N · S · A |
| Add / edit **medications** + view interactions | P · N · A |
| Add / remove **family history** | P · N · A |
| Add / edit / remove **risk factors** | P · N · A |
| Add / edit **clinical alerts** | P · N · A |
| Add / edit **preventive-care activities** | P · N · A |
| Record / edit **vitals** (out-of-range values and a transposed BP are refused with the reason and the accepted range; a height under 10 cm or a weight under 0.1 kg is refused before sending, with a metres-for-centimetres hint; a BMI that cannot be computed is left blank with no weight category) | P · N · A |
| A multi-reading vitals save with one refused field keeps the saved readings, clears them from the form, and names them, so a re-save does not duplicate them | — (automatic) |
| A temperature above 42 °C needs an explicit "confirm this extreme temperature" tick before it saves; a refused save scrolls to the tick and marks it red; form placeholders read "e.g. 72" | P · N · A (automatic) |
| A weight under 2 kg on a patient over 1 year needs a "Confirm this weight is correct" tick | P · N · A (automatic) |
| **Retract** a mis-entered vital from the history (confirm names the reading and its date, and Escape cancels it; the row is marked inactive, the audit trail is kept, nothing is deleted; PHIPA s.55 right of correction) | P · N · A |
| A clinical date is bounded at both ends — refused in the future, and refused before the patient's own birth date (3-day grace); enforced server-side, so it holds whether or not the form was used | — (automatic) |
| Add / remove **procedures** | P · N · A |
| Add / remove **social history** | P · N · A |
| Flag a condition **sensitive** (excluded from provincial sharing) — a failed lock is reported, never silently swallowed | P · A |

### 9.7 Clinical Documentation (Notes & Encounters)
| Feature | Roles |
|---------|-------|
| Start / open an encounter (visit) | P · N · A · R · C |
| **Earlier visit still open** dialog on start: close it as incomplete (not billed, audit-logged, no duration shown) and start today's, or resume it; never resumed silently | P · N · A |
| "Start review" and close an encounter | P · N · A |
| **Resume / End** a stuck in-progress encounter from the visit list | P · N · A |
| **One open visit per patient, per clinician, per day** — every door returns your existing visit instead of stacking a duplicate, and a nurse's visit is never handed to the physician | — (automatic) |
| Cancel an encounter started in error (a refused cancel says the encounter is still open and who can cancel it) | P · N · A · R · C (learners: own encounters only) |
| Close an encounter without billing (learners: own encounters only; a fee code from a nurse or learner is refused with a sentence) | P · N · A · R · C |
| Nurses get no note editor or **+ Note** in an encounter; a note-less encounter reads "No note yet — notes are written by the physician" | — (automatic) |
| Front-desk view of clinical notes follows the practice setting (none / metadata only, the default / full), on the Encounters tab and in the chart PDF | S · `staff` (per setting) |
| A recording with no speech reports "No speech was detected" instead of drafting from noise | — (automatic) |
| Create / edit a draft SOAP note | P · A · R · C (learners only with a confirmed preceptor) |
| Unsaved note text is kept across a chart-tab switch (in memory, per login; cleared on save, sign, discard, or logout; never browser storage) | — (automatic) |
| The browser asks before a reload or tab close while a note has unsaved text (only then); discard and insert-template ask in an in-app dialog | — (automatic) |
| **Sign** a note (immutable snapshot) | P · A |
| Sign as a learner: the note goes to **Pending review** for the preceptor fixed at that moment, never straight to signed (holds even with the flag off) | R · C |
| Request a preceptor from the chart banner (physician list, optional reason); the request reaches that physician's inbox and Dashboard | R · C |
| Confirm or decline a preceptor request (Dashboard **Learner supervision** card; an admin sees every request, each naming its physician) | P · A |
| Set or end a learner's preceptor directly (API only) | P · A |
| Review a pending learner note: **Return with revisions** (back to draft, learner text kept, inbox task to the learner; comment required), **Sign with comment**, or **Sign**; only the note's preceptor co-signs | P |
| Return a pending learner note (admin; co-signing is refused, and the bar says to return it or reassign the preceptor) | A |
| Learner review queue on the Dashboard (practice-wide for admin) | P · A |
| Co-signed learner note shows the learner signature, "Co-signed by" the preceptor's name and role with time and comment, and a "Written by" line naming who wrote each part | — (automatic) |
| **Amend** a signed note | P · A |
| Flag a note **sensitive** | P · A |
| **AI dictation → SOAP structuring** | P · A |
| **AI Scribe** (on-prem Whisper transcription of audio) | P · A |
| A scribe assessment line not supported by the dictation is kept but prefixed "[inferred — not stated in dictation]", with an amber count pill in the editor | — (automatic) |
| A lab or vital the scribe took from the chart, not the dictation, is tagged "(lab <date>)" / "(vital <date>)" in the draft, with a count pill | — (automatic) |
| Scribe screen: instruction-like text in the dictation, or a draft claiming something was already done, raises an amber advisory in the editor (never blocks; audit row holds counts only) | — (automatic) |
| A scribe-drafted note records that it was AI-drafted (one-way) and shows "AI-drafted · reviewed" once signed | — (automatic) |
| The scribe records a patient's request as "Patient requests …", never as an action taken unless the dictation says so | — (automatic) |
| **+ Order bloodwork** / **+ Order imaging** on the encounter bar (tablet width and up), pre-filled with the patient in the room | P · N · A |
| Bloodwork ordered during an open encounter attaches to that visit; lab and imaging orders count as visit actions in the end-of-visit summary and billing rationale | — (automatic) |
| Post-encounter **AI chart suggestions** (SKIPPY) | P · A |
| Note statistics | P · N · S · A |

### 9.8 Prescribing (Rx)
| Feature | Roles |
|---------|-------|
| View a patient's prescriptions (Rx tab) — a secretary is told the tab is restricted rather than shown an empty list | P · N · A · R · C · `staff` |
| Search the drug reference (reports searching / no catalogue match / search failed, so an empty dropdown is never silent) | P · N · S · A |
| Look up **ODB / Limited Use (LU)** codes | P · N · S · A |
| Create a prescription | P · A |
| Modify / cancel a prescription | P · A |
| **Refill** a prescription | P · A |
| **Fax** a prescription to a pharmacy | P · A |
| **Send step** after sign / override-sign / refill — record the destination (fax on file, fax another pharmacy, print, defer, document-only); confirmed in the dialog before it closes; audit-logged | P · A |
| Save a faxed pharmacy as the patient's **preferred pharmacy** from the send modal | P · A |
| The Rx list shows each script's delivery state (Faxed / Queued / Printed / Pending / Documented) without opening it; a printed script no longer reads as queued for fax | P · N · A · `staff` |
| **AI prescription suggestions** from a note's assessment/plan (gated by the chart's AI consent — on a declined chart the note never reaches the model) | P · A |
| **Dose required to sign** (blank or dash-only dose refused on create and modify; "as directed" accepted; refill exempt; the inline hint waits for a catalogue pick or a visited Dose field) | P · A (automatic) |
| **Run-together dose/frequency refused** ("500 mg250 mg", "BID PRNBID") on create, modify, and update, and at fax/print for older scripts; catalogue-prefilled Dose/Frequency select on focus so typing replaces | P · A (automatic) |
| Allergy screening against the chart's allergy list, including allergies typed in without a category | P · A (automatic) |
| Drug-interaction checking against active meds (including trimethoprim-sulfamethoxazole with an ACE inhibitor or ARB, a major hyperkalemia risk) | P · A (automatic) |
| **Class-level interaction screening** — a pair the reference never enumerated is still flagged when the reference already calls that class combination significant (e.g. DOAC × NSAID → apixaban + celecoxib); the warning states it matched on class, and can only add a warning | P · A (automatic) |
| **One warning per interaction** — a molecule held twice under two names (a CPP medication and a prescription) is screened once, so a class warning never contradicts the enumerated warning beside it | P · A (automatic) |
| **Dual antithrombotic warning**: warfarin × DOAC, warfarin × clopidogrel, and DOAC × clopidogrel raise a major bleeding warning in both directions, asking for the indication and a stop date; advisory and overridable with a reason | P · A (automatic) |
| **Triple-whammy check** (NSAID + ACE inhibitor/ARB + diuretic → major AKI warning naming each leg; low-dose ASA excluded) | P · A (automatic) |
| **Drug–disease contraindication screening** against the active problem list — NSAID × CKD, NSAID × heart failure, metformin × renal impairment, beta-blocker × asthma, anticholinergic × dementia; headed *Condition-Based Contraindication* with its severity and the chart entry that fired it. Runs on a typed name as well as a catalogue drug | P · A (automatic) |
| A denial, a query, a "rule out", or a relative's illness on the problem list does not fire a contraindication; an entry with an unknown status is screened rather than dropped | — (automatic) |
| A beta-blocker is graded on selectivity (non-selective in asthma = major; cardioselective = moderate caution) | — (automatic) |
| **Numeric eGFR read from the labs** — the most recent result with its date; < ~30 major, 30–59 dose caution. A low result warns even with nothing renal on the problem list; a reassuring result can never downgrade or suppress a problem-list warning, and a chart/lab disagreement shows both | — (automatic) |
| The contraindication footer names its own evidence — the problem-list entry, or the most recent lab value stated as *not* a problem-list entry | — (automatic) |
| Contraindication screening also runs on **refill and modify**, which show the full warning (label, severity, clinical text) and offer the same reason box | P · A |
| An override on create, **refill, or modify** records the override, the reason given, and the contraindications overridden | P · A (automatic) |
| The off-catalogue advisory names the two kinds of screening apart — allergy and condition-based screening still run, drug-to-drug interaction screening cannot | — (automatic) |
| Every refill count on the Rx tab names itself — the detail grid says **Refills authorized**, the Estimated supply card says how many remain, and the medication-list row says "3 refills authorized" rather than a bare "3 refills" | — (automatic) |
| **Directions kept in step with a superseding script** (template sig rebuilt, own wording substituted, a stale strength cleared and audited) | P · A (automatic) |
| **Unscreened-drug advisory** (free-text drug not in catalogue → forced, audited acknowledgment) | P · A (automatic) |
| **Duplicate-therapy warning** (headed "Same-molecule duplicate", names the molecule; med already active on the CPP **or** as an active prescription → override with audited acknowledgment) | P · A (automatic) |
| **Duplicate-class warning** ("Class-based duplicate": NSAID, SSRI, SNRI, statin, ACE inhibitor, ARB, benzodiazepine, beta-blocker, opioid; SSRI + SNRI and ACE inhibitor + ARB pairs; brand names classified by catalogue; low-dose ASA excluded); overridable, audit-logged | P · A (automatic) |
| Both duplicate checks also run on refill and Modify, leaving out the script being replaced | P · A (automatic) |
| **Side-chain advisory**: cephalexin, cefadroxil, or cefaclor with a high-criticality or anaphylactic penicillin allergy raises an amber confirm suggesting a dissimilar side chain | P · A (automatic) |
| The New Rx form screens only a chosen drug (a catalogue pick, or a typed name committed with Enter or by leaving the field); until then it reads "Choose a drug to screen" | — (automatic) |
| Changing the drug on a form prefilled from a re-prescribe or a documented medication clears its dose, frequency, quantity, and directions | — (automatic) |
| Refill or modify of a script that is no longer active is refused, naming its status and the script that replaced it | P · A (automatic) |
| One prescription per submission: a double press of Sign / Prescribe anyway, or a retry after a lost response, returns the first script | — (automatic) |
| **Attach to today's visit (time)?** on New Rx when the browser lost the open visit but the server still holds one today; ticked by default, never attached silently | P · A |
| Pharmacy fax number validated (dialable NANP, no premium-rate 900 / 976); number and pharmacy stored on the script and audit-logged | P · A (automatic) |
| Hold or discontinue withdraws a queued pharmacy fax (marker reads **Fax withdrawn**); a failed **Resume** shows its reason | P · A |
| **Screening line** (prefixed "Drug–drug:", grey rather than green when an allergy warning sits beside it): the interaction screen states whether it was complete, partial (naming the medications not in the reference), or did not run, on the Rx form before signing, the override dialog, Modify, refill, the Send step, and Inbox refills; information only; the state is always in the audit row; the count is of drugs, so a CPP entry and a script for the same molecule count once | P · A |
| Drug catalogue includes 20 common family-medicine drugs added in October 2026 (iron, cephalexin, metoprolol, tramadol, and others) with 43 interaction pairs; no DIN on these entries | — (reference data) |
| An active script that collides with an allergy carries a **⚠ Allergy** badge in the Rx list | P · N · A · `staff` |
| **Resume (end hold)** on a held prescription | P · A |
| **Allergy status not recorded** prompt at signing on a chart with no allergy entry (Confirm and sign / Go back); the confirmation is audit-logged; never a block | P · A |
| **Dispense-quantity / repeat check** (quantity ≤ 0 or absurdly large, or repeats above 11, refused with the reason) | P · A (automatic) |
| **Documented-not-prescribed** section with "Prescribe & send" (incl. SKIPPY-accepted meds) | P · A |
| A prescription carries a Health Canada DIN only where the drug is matched to a catalogue entry that has one; a script written by name is stored with the drug as text rather than as a coded concept with an empty code | — (automatic) |
| The DIN and the code system that defines it are stored and exported together, or neither is; a re-prescribe by name drops both | — (automatic) |

### 9.9 Labs
| Feature | Roles |
|---------|-------|
| View lab reports, reference intervals, and trend charts (every read audit-logged; a secretary's view is read-only and the pane says so) | P · N · S · A · R · C |
| A load failure reads as a failure ("couldn't be loaded") and hides the "All reviewed" / "Select a report" prompts | — (automatic) |
| Add a lab report | P · N · A |
| **Acknowledge** a result + set follow-up disposition | P · N · A |
| One Acknowledge action per screen — the header button is always there, and the footer button appears only on a report long enough to have scrolled it away | — (automatic) |
| **Critical-value flag + automatic critical inbox item** to the ordering physician, routed on the FHIR **HH** / **LL** codes as well as EMR21's own `critical`; the report still files if routing fails | — (automatic) |
| Critical results badged **CRITICAL HIGH** / **CRITICAL LOW** in solid red, with the raw code on hover | — (automatic) |
| **Qualitative results** (a word such as Positive) stored, shown in the value column, exported to FHIR, and carried into note context | P · N · A |
| A result flagged H / L / HH / LL / critical must carry a value, a word, or a comment behind the flag | P · N · A (automatic) |
| **High / low flag derived from the report's own reference range** when the lab sent no interpretation, so a result filed through the API is flagged like one entered by hand; a flag the lab did send is never overwritten | — (automatic) |
| **Critical-value bands**: an unflagged result crossing its analyte's band (potassium, sodium, glucose, hemoglobin, platelets, WBC, creatinine, calcium, bicarbonate, INR) is marked HH/LL and routed to the physician inbox; a unit the band doesn't accept disables it; no band, no HH/LL | — (automatic) |
| View the practice's critical bands in force | P · N · A |
| Adjust the practice's critical bands (practice settings API; audit-logged) | A |
| A pending requisition reads **Awaiting results** (not unreviewed, no Acknowledge); a cancelled or entered-in-error one reads **Cancelled** and leaves the count | — (automatic) |
| A report with no collection date plots on the trend chart on its reported/filing date | — (automatic) |
| **Coded results** (e.g. SNOMED CT "Positive") show the word with a code badge; FHIR export sends the code only when system and code are both present | — (automatic) |
| A non-finite, out-of-range, or negative-concentration value is refused at filing with a sentence | P · N · A (automatic) |
| A filed result closes the matching pending bloodwork order item (same patient, same LOINC, ordered ≤ 90 days), recording which result closed it; the requisition goes final when every test is back | — (automatic) |
| **AI panel summary** on a filed report, given the report's numeric bounds and barred from calling an in-range value high, low, elevated, or borderline; gated by the chart's AI consent (declined → the summary says AI is off and no values are sent) | — (automatic) |
| An unrecognised interpretation code is refused when the result is filed (N / H / L / HH / LL / critical) | P · N · A (automatic) |
| A result carries a number **or** a word, not both — a report filed with both is refused at entry rather than losing the number on export | P · N · A (automatic) |
| The interpretation flag exports as an HL7 v3 coded concept (with the original letters kept as text), so a receiving system can filter on it | — (automatic) |
| A result row can be filed without a LOINC code (matched by name off-catalogue); a resulted report with no results is refused | P · N · A (automatic) |
| **Result callbacks**: record "patient aware" or issue a callback with an owner on a reviewed result; close a callback (API only) | P · N · A |
| Log a callback contact attempt (who / when / method / outcome); 3 failed attempts or 14 days with no booked appointment returns it to the provider (API only) | P · N · S · A |
| List a patient's callbacks, or the practice-wide callback list with per-status totals (API only) | P · N · S · A |
| Run the callback sweep that returns stale callbacks to their provider (API only) | P · A |

### 9.10 Imaging & Documents
| Feature | Roles |
|---------|-------|
| Upload a document (drag-and-drop, PDF/JPEG/PNG/TIFF ≤ 25 MB) | P · N · S · A |
| View / download a document (secure presigned URL) | P · N · S · A |
| Categorize / edit document metadata | P · N · S · A |
| View imaging (DI) reports | P · N · S · A |
| Delete a document | P · A |

### 9.11 Immunizations
| Feature | Roles |
|---------|-------|
| View immunization record (CVX-coded) | P · N · S · A |
| Record an immunization | P · N · A |
| The administered date cannot be in the future, and cannot precede the patient's birth date | P · N · A (automatic) |
| Dose # in a series must be a whole number from 1 to 10 (form and server) | P · N · A (automatic) |
| Remove an immunization dose (in-app confirm; removed from the record, retained in the audit trail; the control is hidden for roles that can't use it) | P · A |

### 9.12 Referrals, Care Team & Correspondence
| Feature | Roles |
|---------|-------|
| View a patient's referrals | P · N · S · A · R · C |
| Create / edit referrals | P · N · A |
| Generate referral & consult **letters**, attach labs/docs | P · N · A |
| **AI letter drafting**, written from the chart (demographics, active problems, meds, allergies; empty sections stated as empty; bracketed placeholder in place of anything the chart doesn't hold; no health card / address / phone sent) | P · N · A |
| A draft the model didn't return is reported as unavailable and leaves your typed text alone | — (automatic) |
| On a chart without AI consent the letter's **✦ AI Draft** button is greyed out up front with the reason | — (automatic) |
| The draft never asserts a clinical negative from an unrecorded finding (no side effects / no allergies / normal exam) — it writes a bracketed placeholder instead, and reports only the chart's explicitly empty sections, as "none recorded" | — (automatic) |
| Send / lock correspondence | P · N · A |
| Flag correspondence/referral **sensitive** | P · A |
| **Referral Tracker**: mark-sent, **chase**, record reply | P · N · S · A |
| Recording a reply with no appointment date asks once (**Save without a date**); a failed save says the referral is still on the chase list | — (automatic) |
| Chasing keeps the referral's original send date (the chase is stored as the reminder time) | — (automatic) |
| **AI parse** of an incoming referral reply | P · N · S · A |
| Manage the **referral-location directory** | P · N · S · A |
| Manage a patient's **care team / specialists** | P · N · A |
| End a provider's care-team membership (× on the card; kept in the chart history with its end date; audit-logged) | P · N · A |

### 9.13 Reminders — Recalls & Preventive Care
*(Nav label: **Reminders**; page title: Reminders & Recalls; categories: Recalls · Preventive · Tasks.)*
| Feature | Roles |
|---------|-------|
| View recalls/tasks (overdue/upcoming) + stats | P · N · S · A |
| The Dashboard's overdue-recalls count is a physician's own recalls; for nurse, secretary, staff, and admin it is practice-wide, matching the Reminders page | — (automatic) |
| Create / edit a patient recall | P · N · A |
| Create a **personal task** (category Task, no patient, own list only; **+ New task** for front desk, refused by name if a patient recall is attempted) | P · N · S · A |
| Mark done / dismiss a personal task of your own; on a patient recall those controls stay with the clinical roles and are hidden from front desk | P · N · S · A (own tasks) / P · N · A (recalls) |
| A recall with a reminder sent stays outstanding (list, stats, Panel) and carries a **Reminder sent** pill with the date | — (automatic) |
| Task title and custom recall type capped at 200 characters with a live counter | — (automatic) |
| **Send** a recall to a patient (front desk sees Send on patient recalls; Done/Dismiss stay hidden for them) | P · N · S · A |
| After Done the list reloads so a recurring recall's next copy shows; a recurring close never stacks a duplicate open recall; reopening clears the completed date | — (automatic) |
| **Generate recalls** from guideline logic | P · N · A |
| A generated recall is dated against the chart (the model's date is accepted only within a plausible window for that interval, otherwise computed from the last signed note), owned by the patient's primary physician, and carries a repeat interval read from its type by whole words (HPV vaccine = 12 months, not the 36-month Pap cadence) | — (automatic) |
| A recall's type and note are length-bounded on the API (200 and 2,000 characters), matching the one-line fields the screen offers | — (automatic) |
| A due date in the past shows an amber "This date is in the past." hint (still allowed) | — (automatic) |
| Creating a recall when the same type is already open for the patient warns in the window (the recall is still created; the audit row counts the open duplicates) | P · N · A |
| An optional reason on a recall status change is appended to the recall's notes and recorded as given in the audit row (API only) | P · N · A (recalls) · S (own tasks) |
| **Recall ↔ appointment link**: a booking linked to a reminder takes it off the overdue list while the appointment is live; a cancel or no-show puts it back with a note (API only) | P · N · S · A |

### 9.14 Panel / Population Health
| Feature | Roles |
|---------|-------|
| Open the Panel page (two tiers: every clinic role sees the counts and practice summary; the patient-naming views are drawn for clinical roles only, and the page says so) | P · N · S · A |
| Panel stats & summary (counts only, nobody named, incl. AI panel summary; the summary excludes archived charts and names its scope) | P · N · S · A |
| High-risk patient list (+ Export CSV) | P · N · A |
| Preventive-care **gaps** | P · N · A |
| Practice-wide view + provider / demographic (sex, age) filters (the provider list includes the signed-in clinician) | N · A (physician = own panel) |
| **Cohort builder** — find patients by disease / lab (incl. abnormal-only) / keyword, AND-combined within scope | P · N · A |
| Export the cohort list as **CSV** | P · N · A |
| Anything that names patients with their diagnoses, lab values, or risk scores is limited to the roles with a treatment relationship (PHIPA minimum-necessary); the aggregate counts stay open to every clinic role | — (automatic) |
| **Batch chart export** — a `.zip` of one chart PDF or one FHIR R4 Bundle per patient in the cohort (audit-logged) | P · A |
| Generate recalls across the panel | P · A (and the generic `staff` login) |

### 9.15 Billing (OHIP)
| Feature | Roles |
|---------|-------|
| View billing claims + stats | P · N · S · A |
| Approval-rate card is approved ÷ adjudicated this month, names its denominator, and shows a dash when nothing is adjudicated (drafts not counted) | — (automatic) |
| **AI billing-code suggestion** (incl. time-based K-codes; modality-aware — virtual visits steered off in-person A-codes) | P · A |
| The code suggestion is gated by the chart's AI consent before the note is read, and the AI read of a signed note is audit-logged (no note text, no health-card number) | — (automatic) |
| A physician with no CPSO number on file gets the clinical summary with the billing fields empty and no OHIP code suggested, and no counselling-time prompt; manual claim entry is unaffected | — (automatic) |
| Editing the suggested code withdraws the AI rationale as its justification — the drawer says which code the rationale was written for and asks for one covering the new code; a physician's own typed rationale is untouched | — (automatic) |
| Create / edit a claim (a hand-entered service date cannot be later than today) | P · A |
| **OHIP diagnostic code** (3 digits) required to bill from the drawer and to submit; pre-filled from the practice default; shown on every claim row; editable on drafts (**+ Add diagnostic code**); submit refuses a claim without one | P · A |
| **Fee-code validation** (a code outside EMR21's catalogue is refused; the refusal can show the catalogue in place; the fee comes from the catalogue, not the request) | P · A (automatic) |
| **Claim-field validation** (a claim whose fields EMR21 does not recognise is refused, naming the field, instead of saving an empty $0 draft that reports success) | P · A (automatic) |
| Submit a claim | P · A |
| **Void / reverse** a claim (preserves history, drops out of billed/paid stats) | P · A |
| A demo-adjudicator rejection is labelled **Simulated**, with the full reason on hover, so it never reads as a real OHIP response | — (automatic) |
| End-of-visit billing confirmation drawer (falls back to manual entry when AI is unavailable or the chart declined AI); ✕ or Escape leaves the visit in progress | P · A |
| A nurse's or learner's drawer has no code field and offers only Close without billing, saying the physician submits the claim | N · R · C |
| The draft banner and **Submit all** count every draft claim, not only the first page loaded | — (automatic) |
| The drawer names the reason it has no suggestion — "AI is off for this patient" on a consent-declined chart, "unavailable" only when something failed | — (automatic) |
| On a chart known to have AI off, ending the visit opens the manual drawer without requesting a summary; where the consent on hand belongs to a different patient (ending from the worklist or another chart) EMR21 asks and the refusal lands in the same drawer | — (automatic) |

### 9.16 Forms (eForms & Official Fillable PDFs)
| Feature | Roles |
|---------|-------|
| Browse clinic-authored form templates (active) | P · N · S · A |
| **AI prefill** a form from the chart | P · N · S · A |
| Submit / store a completed form | P · N · S · A |
| **PHQ-9 scoring**: total /27 and severity band once all nine items are answered, item-9 flag, real question text; submit confirm names unanswered items and a positive item 9; collapsed completed row shows total, band, item-9 badge (arithmetic, never AI) | — (automatic) |
| View a patient's completed forms | P · N · S · A |
| **Print** a completed form (PDF) | P · N · S · A |
| **Fax / email** a completed form | P · N · A |
| Browse **official fillable forms** (Ontario/CRA/agency blanks) | P · N · S · A |
| **Fill** an official form with chart data (PDF) | P · N · S · A |
| Admin: enable / disable a form template | A |
| Admin: ingest an official blank PDF (introspect AcroForm fields) | A |
| Admin: map official-form fields to chart data | A |

### 9.17 SKIPPY & AI Assistance
| Feature | Roles |
|---------|-------|
| Ask SKIPPY clinical/workflow questions (context-aware) | P · N · S · A |
| Chart-grounded answers: scope binds to the chart on screen, the record is read at ask time, empty sections are stated as empty, documented meds stay separate from prescriptions written here, and the chart read is audit-logged | P · N · S · A (automatic) |
| The chart SKIPPY reads carries preventive care and immunizations, with overdue computed the same way the Preventive Care panel computes it, so "what is overdue?" is answered from the chart rather than from a gap in it | P · N · S · A (automatic) |
| Post-encounter medication suggestions match the chart on the drug name, not the whole label; a card the server refuses as a duplicate reads "already on the chart — not added again" instead of reporting a row that was never written | P · A (automatic) |
| A suggested problem's quote must evidence that problem, not merely be verbatim in the note; EMR21 re-selects a supporting sentence (usually the assessment line) where the model's quote is about something else, and drops the card when the note supports nothing | P · A (automatic) |
| Quote matching sets aside shape-of-problem words (exacerbation, chronic, stage, disease, type) and requires the illness itself in the quote — a one- or two-word diagnosis needs every identifying word, so "fibrillation" alone does not support "Atrial fibrillation" | P · A (automatic) |
| Absence-of-finding phrasing counts as a denial — *absent, absence of, unremarkable, free of, ruled out* alongside the existing cues; a finding that is itself an absence is therefore also dropped and must be added by hand | P · A (automatic) |
| Duplicate detection reads a problem's identifying words in any order and ignores inert qualifiers (stable, compensated, controlled, unspecified, ongoing, established, known, longstanding, baseline, current), so a front-loaded qualifier no longer slips a restatement through; *uncontrolled*, *decompensated* and a different stage number are untouched | P · A (automatic) |
| A suggested problem arrives unconfirmed, with its ICD-10-CA code resolved from the curated list the manual typeahead uses and left off when the wording matches more than one code | P · A (automatic) |
| A problem the chart already states more precisely suppresses the suggestion; a suggestion that adds specificity, or that matches only a resolved or inactive problem, still reaches you | P · A (automatic) |
| Inside a chart, the next-patient chip names no one and says why it is blank, and the sign-in greeting above it refers to the next patient by role rather than by name | — (automatic) |
| Encounter summary checked against the visit's own action count; a "nothing recorded" claim is replaced by a sentence built from the audit rows | P · A (automatic) |
| Post-sign **BP backstop**: when the model returned no blood-pressure vital, a reading written in the note ("BP 128/76", "128 over 76") is offered as one suggestion through the same checks; still needs Accept | P · A (automatic) |
| Dashboard AI-status badge: checking / warming up / active / offline, from a live model probe | P · N · S · A |
| Encounter timer + time-based billing prompts | P · A |
| AI encounter/visit summary | P · A |
| Personality modes (Professional / Skippy / Chill / Haiku) | P · N · S · A |
| Capture & review **AI validation** outcomes + stats | P · N · A |

> **AI is additive, never required.** Every AI-assisted action above has a fully manual path, and a patient's chart-level AI consent gates all AI features for that patient — declined means AI off, with 100% of EMR functionality still available manually.

### 9.18 Consent & Privacy
| Feature | Roles |
|---------|-------|
| Record a patient consent event | P · N · S · A |
| Check consent status | P · N · S · A |
| Check **AI-consent** status (master AI gate) | P · N · S · A |
| Emailed **intake invite** + electronic consent capture | P · N · S · A |
| Append-only audit logging of all clinical access (the database refuses updates and deletes on the log) | — (automatic) |
| Refused requests logged as ACCESS_DENIED | — (automatic) |
| Patient reads and writes record the acting user's email and role alongside the account id | — (automatic) |
| Direct identifiers (name, DOB, address, telecom, health card) redacted from audit-log values; field names kept | — (automatic) |
| OHIP encryption at rest + masking in logs | — (automatic) |

### 9.19 Data Export & Interoperability
| Feature | Roles |
|---------|-------|
| Export a patient chart as **PDF** | P · N · S · A |
| Export a patient as **FHIR** (R4-aligned JSON) | P · A |
| FHIR **MedicationRequest** carries the prescription's DIN as a coded concept where the script has one, alongside the drug text | — (automatic) |
| FHIR export carries the care team as **CareTeam** resources (current members active; ended members in an inactive CareTeam with their end date), single and batch export | — (automatic) |
| FHIR **Observation** sends a coded lab result as valueCodeableConcept only when it has both system and code; otherwise valueString | — (automatic) |
| Export many charts at once from a Panel cohort — `.zip` of PDFs or FHIR Bundles | P · A |
| Export the full patient list as **CSV** | A |

### 9.20 Administration & Practice Settings
| Feature | Roles |
|---------|-------|
| User management (create/edit/deactivate, roles, password resets) | A |
| View audit log | A |
| **Show background polls** in the Audit Log: the app's minute-by-minute inbox/schedule poll reads are filed in their own log, readable and exportable with the same filters; the export's audit row names which log was taken | A |
| **Feature flags** (Settings): practice-wide switches, on by default, hide a surface for the whole practice without a deploy; each change is audit-logged naming the flag. Sixteen today: Guided tour, Server-side sessions, Audit poll split, Error boundary, Personal layout, Critical-value bands, Inbox → lab report link, Coded lab results, Outstanding orders, Order reconciliation, Result callbacks, Recall ↔ appointment link, Result escalation, Flowsheets, Screening honesty states, Learner supervision. A switched-off surface's message points at Settings → Feature flags | A |
| Read the practice's effective feature flags at login (so the UI can gate surfaces) | P · N · S · A · R · C |
| Per-user UI preferences stored on the server (own row only; not a clinical audit event): collapsed menu, default pinned chart panel | P · N · S · A · R · C |
| Pin a chart panel on this chart only (per user, this browser) or as my default on every chart (desktop) | P · N · S · A |
| Overdue limits for orders per type (bloodwork / imaging / referrals, 1–365 days; practice settings API; audit-logged) | A |
| View practice profile | P · N · S · A · R · C |
| Edit practice settings | A |
| Clinic hours listed Monday to Sunday; a closing time not after the opening time (or not HH:MM) is refused with a sentence; a closed day is exempt | A (automatic) |
| Appointment-type length bounded to 5–240 minutes; the refusal names the type | A (automatic) |
| Practice OHIP billing number must be exactly 6 digits (the server refuses anything else; Settings blocks a partial number before saving; empty clears it) | A (automatic) |
| Default diagnostic code for new claims (Settings, Billing section; pre-fills the end-of-visit drawer) | A |
| **Front-desk access to clinical notes** (Settings): No access / Metadata only (default) / Full, for secretary and staff accounts | A (others view) |
| Session timeout (Security & Privacy) enforced as the idle sign-out | A |
| **Import charts from another EMR** (OntarioMD CDS XML — PS Suite/OSCAR/TELUS CHR/Accuro; preview → commit, duplicate handling) | A |
| View showcase-site visit metrics (analytics summary for the public emr21.ca site; no clinic-app screen) | A |

### 9.21 Patient Portal
| Feature | Roles |
|---------|-------|
| Portal login (separate from clinic app; a wrong credential is reported on the form; a DOB before 1900 or after today is refused in-app; locks ~15 min after 5 failed attempts) | ▢ |
| **My Requests**: own refill / referral / sick-note / message requests with submitted · in review · done; recent requests also shown under the Requests form; an open refill keeps "Requested ✓" on the medication | ▢ |
| Read the clinic's **reply** under the request it answers (read receipt back to the clinic) | ▢ |
| Request subject capped at 120 characters and details at 5,000, each with a live counter; a server refusal shows its reason | ▢ |
| Sign-in email matched without regard to case or spaces | — (automatic) |
| When an email and date of birth match more than one chart, the portal asks for the patient's first name and signs in only on exactly one match | ▢ |
| View **My Health** summary | ▢ |
| **Book appointments** | ▢ |
| Submit **intake forms** (incl. emailed-invite intake, each answer capped at 5,000 characters with a counter); a completely blank questionnaire is refused | ▢ |
| Send **requests/messages** to the practice (land in clinic inbox; with no primary physician they go to the front desk) | ▢ |
| The same request sent twice within a minute is filed once; same subject with different details is a new request | — (automatic) |
| The `?demo` portal sign-in works only where the server allows it (never in production); otherwise, or after 8 s with no answer, the sign-in form shows | — (automatic) |
| View own visit history — finished encounters as well as completed appointments, so a walk-in or phone visit is visible; date, visit-type label, and status only, never the clinician's reason for visit | ▢ |

### 9.22 Outstanding Orders (Order Tracking)
*(Nav label: **Orders**; page title: Outstanding orders. Tracks outbound referrals, bloodwork, and imaging awaiting a result/reply.)*
| Feature | Roles |
|---------|-------|
| View the outstanding-orders worklist (filter by status + kind) | P · N · S · A |
| **Order bloodwork** (multi-test; LOINC-coded; the list includes Urine ACR and NT-proBNP, and an **Other (specify)** line carries a test that isn't listed) | P · N · A |
| **Order imaging** (X-ray/US/CT/MRI/echo/mammo/DEXA, and only those; a blank or free-text modality is refused); body region and clinical indication are both required, in the modal and on the server | P · N · A |
| Order placement is audit-logged as CREATE on the imaging or lab order itself | — (automatic) |
| A bloodwork order placed with an encounter open attaches to that visit | — (automatic) |
| **Cancel** a bloodwork or imaging order with a reason while nothing has come back (refused once a result is in); kept on the worklist as Cancelled for 30 days; audit-logged | P · N · A |
| **Overdue** card: orders waiting past the practice's per-type limit (defaults 14 d bloodwork, 30 d imaging and referrals), most overdue first, with days waiting and days past the limit; opened by `?view=overdue` and the Dashboard's Overdue orders tile | P · N · S · A |
| Double-submit guard on bloodwork and imaging orders (one order per click; an identical order within 2 minutes is ignored) | — (automatic) |
| A filed lab result closes the matching pending test on the worklist | — (automatic) |
| Order modals prefill the open encounter's patient (name + DOB on the chip, always changeable) | P · N · A |
| Order from the chart: **+ Order bloodwork** on the Labs tab, **+ Order imaging** on the Imaging tab, both on the encounter bar; same modals, patient pre-filled "From this chart" | P · N · A |
| **Withdraw remaining (n)** on a partly-resulted bloodwork order: results kept, outstanding tests marked withdrawn with a reason, requisition goes final; audit-logged | P · N · A |
| Print a **lab requisition** PDF (health-card number masked) | P · N · A |
| Print an **imaging requisition** PDF (health-card number masked) | P · N · A |
| Mark **Result in** — reconcile a returned result/reply (whole order or a single test) | P · N · A |
| View returned lab values **inline** on a results-back row (value, reference range, abnormal flag) | P · N · S · A |
| **Acknowledge** a returned result inline (warns when tests are still pending) | P · N · A |
| **Message staff** — route a triaged task from an order to the Staff Inbox | P · N · A |
| Action / dismiss an order | P · N · A |

---

## 10. Features by User Type

A role-by-role view of the same capabilities. Use this to understand what each login can and cannot do.

### 10.1 Physician (`physician`)
The most capable clinical role — the full workstation.
- Everything clinical: full chart access, register/edit patients (the chart's Edit info window covers name, DOB, contact details, and health card), build the CPP, record vitals/immunizations/labs. A vital recorded in error can be retracted from the history, which marks it inactive and keeps the audit trail.
- **Documentation:** start/close encounters, write, sign, and amend SOAP notes, use AI dictation/scribe and post-encounter chart suggestions. A scribe-drafted note keeps an AI-drafted badge after signing.
- **Prescribing:** create, modify, refill, cancel, and fax prescriptions, each ending on a send step that records where the script went; a screening line that says whether the interaction screen was complete, partial, or did not run; hold or discontinue a script when a newly recorded allergy collides with it, and resume a held one; AI suggestions; allergy and drug-interaction checks, including the NSAID + ACE inhibitor/ARB + diuretic combination, trimethoprim-sulfamethoxazole on a RAAS blocker, class combinations the reference calls significant even where the exact pair isn't listed, and two antithrombotics from different classes (warfarin, a DOAC, clopidogrel), with a forced advisory when a free-text drug can't be screened; same-molecule and same-class duplicate warnings; a prompt to confirm allergy status on a chart with none recorded; a dose is required to sign; complete pharmacy refill requests inline from the inbox, with the warnings named in full and a record of what each request already refilled.
- **Condition-based prescribing checks:** the drug is screened against the active problem list and against the patient's most recent eGFR, covering NSAIDs in kidney disease and heart failure, metformin in renal impairment, beta-blockers in asthma, and anticholinergics in dementia. The warning names its severity and quotes the evidence that fired it, runs on refills and dose changes as well as new scripts, and never blocks; proceeding past it records the override, the reason, and the contraindications overridden.
- **Orders & results:** place, cancel, and print requisitions for bloodwork and imaging orders (from the Orders page, the chart's Labs and Imaging tabs, or the encounter bar), withdraw the outstanding tests on a partly-resulted order, and track them on the Orders worklist (including the Overdue list) until the result comes back; read and acknowledge returned lab values inline on the worklist; acknowledge labs and set dispositions; manage imaging/documents. Band-derived critical results reach the physician inbox, and a critical item closed with No F/U needs a confirmation. Through the API: result callbacks, the escalation and callback sweeps, flowsheets, and a patient's own flowsheet targets.
- **Referrals & letters:** create referrals, draft (incl. AI) and send letters, run the referral tracker, manage the care team.
- **Forms:** complete clinic-authored forms and fill official government/agency PDFs from the chart, AI-prefill, print, and fax/email.
- **Recalls & panel:** create/generate recalls, work the panel and prevention gaps, build cohorts and export them (list CSV, or a zip of chart PDFs / FHIR Bundles).
- **Billing:** AI code suggestions, create/submit claims, end-of-visit billing confirm (a code is required to bill, it has to be one in EMR21's catalogue, and closing without billing is its own button). A physician with no CPSO number on file is treated as billing outside Ontario and is not offered OHIP codes or the counselling-time prompt; manual claim entry still works.
- **Inbox & messaging:** clinical inbox with AI triage, messages.
- **Sensitivity:** can flag notes/conditions/correspondence as sensitive.
- **Supervising learners:** confirm or decline preceptor requests from the Dashboard, and review a learner's pending notes (return with revisions, sign with comment, or sign). Only the learner's own preceptor can co-sign.
- **Refills from the portal:** Approve & tell the patient sends the refills, replies in the portal, and files the request in one step.
- **Export:** patient PDF and FHIR.
- **Cannot:** manage other users, edit practice settings, or export the full patient CSV (admin only).

### 10.2 Nurse (`nurse`)
Clinical support — most clinical data entry, but not physician sign-off or prescribing.
- Register and edit patients, including the chart's Edit info window; build and edit the CPP (problems, allergies, meds, vitals, history, risk factors, alerts, preventive care). Vitals entry refuses an out-of-range or transposed reading and says which value it objected to, and it refuses a weight or height of 0. A reading recorded in error can be retracted from the history, which marks it inactive and keeps the audit trail.
- Record immunizations and labs; acknowledge results; place, cancel, withdraw, and reconcile bloodwork/imaging orders on the Orders worklist or from the chart, print their requisitions, and action returned results there. Can read the practice's critical-value bands and result-escalation rules, and can be named in the on-call pool or a coverage chain. Through the API: issue and close result callbacks, and view flowsheets and add hand entries (setting a patient's own target is physician/admin).
- Recording an allergy that collides with an active prescription shows the warning, but holding or discontinuing the script is left to a physician or admin.
- Start, cancel, and close encounters. There is no note editor for a nurse inside an encounter, and the billing drawer offers only Close without billing, since the physician submits the claim.
- End a provider's membership of the care team, and send or revoke a patient's intake link from the Forms tab.
- Create/edit referrals and letters (incl. AI drafting), manage the care team, run the referral tracker.
- Forms: complete and fill forms, AI-prefill, print, and fax/email (nurses may send).
- Create/generate/send recalls; schedule and manage appointments (practice-wide day view with provider filter); build and export Panel cohort lists (batch chart export is physician/admin).
- Staff Inbox, AI draft replies, assign inbox items to a clinician, reply to portal patients from the Inbox, messaging.
- **Cannot:** create/submit prescriptions, bill a visit or create/submit billing claims, write, sign, or amend notes, flag data sensitive, manage users, or mark a patient deceased.

### 10.3 Secretary (`secretary`)
Front-desk / administrative.
- Full scheduling: book (incl. urgent), reschedule, cancel (with a confirm), no-show, mark Arrived and Complete, AI slot suggestions; sees the practice-wide day with provider labels and a provider filter. Starting the encounter is the clinician's step; the card says so.
- Register new patients; view charts and demographics; email or revoke a patient's intake link from the Forms tab.
- Clinical notes: what shows depends on the practice's front-desk note access setting. By default a secretary sees each note's type, status, author, and dates but not its text.
- Staff Inbox, assign/triage items, AI draft replies, messaging. Portal requests from patients with no primary physician land with the front desk.
- Send recall reminders from the Reminders page (the **Send** button on each patient recall); keep a personal task list (**+ New task**, mark own tasks done or dismissed); run referral-tracker chase/record-reply; browse referral directory; view the Orders worklist and its Overdue list (placing, cancelling, and reconciling orders is clinician-only). Through the API, log contact attempts on a result callback and link a booking to a reminder.
- Panel: the counts and the practice summary only. The high-risk list, the preventive-care gaps, and the cohort builder name patients alongside their diagnoses and lab values, so the page shows a card saying those are for clinical roles.
- Chart tabs: the Rx tab reads as restricted rather than empty, so a chart with active scripts is never mistaken for one with none. The Labs tab is readable (list and report view, each read audit-logged) and says it is read-only: acknowledging stays with the clinical roles.
- Forms: browse, AI-prefill, submit, fill official forms, and **print** them (but not fax/email — sending is physician/nurse/admin); document upload/view.
- **Cannot:** start an encounter from the schedule, edit clinical CPP data, author notes, prescribe, read a patient's prescriptions, acknowledge a lab result, bill, create a patient recall, fax/email forms, edit demographics beyond creation, see the Panel's patient-naming lists, or access admin/practice settings.

### 10.4 Admin (`admin`)
Everything a physician can do **plus** practice administration.
- All clinical capabilities (admin is treated as a clinician across the API).
- **User management:** create/edit/deactivate accounts, assign roles (including Resident and Clinical clerk), reset passwords. On a new installation the first admin is created at `/setup`.
- **Audit log:** viewer with filters (date range, user, patient, action, resource type), server-side paging, and CSV export of the current filter. The export is recorded in the log and carries access metadata only. **Who accessed this patient** gives a per-user report for one chart. Refused requests appear as ACCESS_DENIED rows.
- **Learner supervision:** sees every preceptor request and pending learner note in the practice, and can confirm requests and return a pending note. Co-signing is left to the learner's preceptor.
- **Practice settings** editing, including clinic hours, appointment types, front-desk access to clinical notes, the enforced session timeout, and the **Feature flags** section (sixteen practice-wide switches, audit-logged) and the Audit Log's **Show background polls** view. Through the practice settings API: critical-value bands, the overdue limits for orders, and the result-escalation rules (unreviewed-days limit, on-call pool, coverage chains).
- **Sessions:** end every session of one user in the practice, or sign the whole practice out (API only for now; audit-logged).
- **Forms library management:** enable/disable templates, ingest official blank PDFs, and edit field maps.
- **EMR chart import:** migrate patients from another Ontario EMR via OntarioMD CDS XML (preview then commit, with duplicate handling) — admin-only.
- **Full patient CSV export** (admin-only), plus batch chart export from a Panel cohort (physician/admin).
- **Showcase-site metrics:** view the visit-analytics summary for the public emr21.ca site (admin-only; no clinic-app screen).

### 10.5 Resident (`resident`) and Clinical clerk (`student`)
Learners on placement. The two roles have the same access; the app labels them resident and clinical clerk.
- Find and open charts, and read labs, prescriptions, and referrals. Writing to those areas stays with the clinical roles.
- Start and update encounters, and write notes, once a preceptor has confirmed. With no preceptor, the chart banner offers a physician list and a **Request** button, and saving or signing a note is refused until the physician confirms.
- Signing sends the note to Pending review. The preceptor returns it with revisions (it comes back as a draft, with an inbox task carrying the comment), signs it with a comment, or signs it.
- Cancel, or close without billing, an encounter they started themselves. The billing drawer has no code field for a learner; the supervising physician submits the claim.
- Keep their own UI preferences (collapsed menu, default pin), and list or end their own sessions. A resident's account settings show the CPSO number field.
- **Cannot:** prescribe, order, bill, edit CPP data, record vitals or immunizations, acknowledge results, or sign a note without the preceptor's review. Most pages outside the chart (the schedule, the Panel, billing, orders) are not open to these roles yet.

| Action | Resident | Clinical clerk |
|--------|:--------:|:--------------:|
| Open charts; read labs, Rx, referrals | ✅ | ✅ |
| Start / update an encounter | ✅ | ✅ |
| Write a note (preceptor confirmed) | ✅ | ✅ |
| Sign a note (goes to Pending review) | ✅ | ✅ |
| Cancel / close without billing an encounter they started | ✅ | ✅ |
| Bill a visit | — | — |
| Request a preceptor | ✅ | ✅ |
| Review another learner's note | — | — |
| CPSO number in account settings | ✅ | — |

### 10.6 Portal Patient (`portal_patient`)
Patient-facing, fully separate from the clinic app.
- Log in at `/portal/login`; see only **My Health**, **Requests**, and **Intake**. Where an email and date of birth match more than one chart, the portal also asks for the patient's first name.
- Book appointments, submit intake forms (including via emailed invite with electronic consent), send requests/messages that arrive in the clinic inbox, follow them under **My Requests** (submitted, in review, done) with the clinic's replies underneath, and view own visit history. Visit history covers finished encounters as well as booked appointments, so a walk-in or phone visit shows up, carrying the date, a visit-type label, and the status.
- **Cannot** see any other patient's data or any internal clinic screen.

### 10.7 Capability matrix (key actions)

Residents and clinical clerks are covered by their own table in §10.5.

| Action | Physician | Nurse | Secretary | Admin | Portal |
|--------|:--------:|:-----:|:---------:|:-----:|:------:|
| View charts / CPP | ✅ | ✅ | ✅ | ✅ | own only |
| Edit demographics (chart **Edit info**) | ✅ | ✅ | — | ✅ | — |
| Edit CPP clinical data | ✅ | ✅ | — | ✅ | — |
| Retract a mis-entered vital | ✅ | ✅ | — | ✅ | — |
| Write & sign notes | ✅ | — | — | ✅ | — |
| Read note text | ✅ | ✅ | per practice setting (default: metadata only) | ✅ | — |
| Close an encounter without billing | ✅ | ✅ | — | ✅ | — |
| Bill a visit from the end-of-visit drawer | ✅ | — | — | ✅ | — |
| Co-sign a learner's note (own learner only) | ✅ | — | — | — | — |
| Prescribe (create/refill/fax) | ✅ | — | — | ✅ | — |
| View lab results | ✅ | ✅ | read-only | ✅ | — |
| Acknowledge labs | ✅ | ✅ | — | ✅ | — |
| Schedule appointments | ✅ | ✅ | ✅ | ✅ | book own |
| Start Encounter from the schedule | ✅ | ✅ | — | ✅ | — |
| Recalls (create/generate; + New reminder button) | ✅ | ✅ | — | ✅ | — |
| Personal tasks (+ New task; done/dismiss own) | ✅ | ✅ | ✅ | ✅ | — |
| Reply to a portal patient from the Inbox | ✅ | ✅ | — | ✅ | read |
| Billing claims | ✅ | — | — | ✅ | — |
| Referrals & letters | ✅ | ✅ | track only | ✅ | — |
| End a care-team membership | ✅ | ✅ | — | ✅ | — |
| Email / revoke a new-patient intake link | ✅ | ✅ | ✅ | ✅ | — |
| Place / cancel / reconcile orders (bloodwork, imaging) | ✅ | ✅ | view only | ✅ | — |
| Withdraw the outstanding tests on a partly-resulted order | ✅ | ✅ | — | ✅ | — |
| Hold / discontinue a script after an allergy collision | ✅ | — | — | ✅ | — |
| Close a critical inbox item with No F/U (with confirmation) | ✅ | ✅ | ✅ | ✅ | — |
| Result callbacks: issue / close (API) | ✅ | ✅ | — | ✅ | — |
| Result callbacks: log a contact attempt (API) | ✅ | ✅ | ✅ | ✅ | — |
| Flowsheets: view, hand entries (API) | ✅ | ✅ | — | ✅ | — |
| Flowsheets: set a patient's own target (API) | ✅ | — | — | ✅ | — |
| Run the escalation / callback sweeps (API) | ✅ | — | — | ✅ | — |
| Set result-escalation rules (API) | — | — | — | ✅ | — |
| Send a recall reminder | ✅ | ✅ | ✅ | ✅ | — |
| Open the Panel page | ✅ | ✅ | ✅ | ✅ | — |
| Panel: named patient lists (high-risk, gaps, cohort) | ✅ | ✅ | — | ✅ | — |
| Panel: counts & practice summary | ✅ | ✅ | ✅ | ✅ | — |
| Read a patient's prescriptions (Rx tab) | ✅ | ✅ | — | ✅ | — |
| Forms (fill / print) | ✅ | ✅ | ✅ | ✅ | — |
| Fax / email forms | ✅ | ✅ | — | ✅ | — |
| Manage forms library | — | — | — | ✅ | — |
| Flag data sensitive | ✅ | — | — | ✅ | — |
| Mark patient deceased | ✅ | — | — | — | — |
| User management | — | — | — | ✅ | — |
| End own sessions / sign out everywhere (API) | ✅ | ✅ | ✅ | ✅ | — |
| End another user's or the whole practice's sessions (API) | — | — | — | ✅ | — |
| Set critical-value bands / overdue limits (API) | — | — | — | ✅ | — |
| Query / export the audit log | — | — | — | ✅ | — |
| Import charts from another EMR | — | — | — | ✅ | — |
| Practice settings (incl. feature flags, front-desk note access, session timeout) | view | view | view | ✅ | — |
| Export FHIR / PDF | ✅ | PDF | PDF | ✅ | — |
| Batch export a cohort's charts (.zip) | ✅ | — | — | ✅ | — |
| Export full patient CSV | — | — | — | ✅ | — |

---

*This manual reflects EMR21 as of the date above. Modules and labels evolve as the product is polished; the live left-navigation, chart tabs, and the role checks in the backend API are always authoritative.*
