Patients confirm, move, cancel and book on their own.
Every visit email opens that one visit, without an email or a code: confirm, move or cancel, with no limits unless the clinic sets them. Signed in, the portal does the same and books new visits through the region’s patient widget. The desk sees who changed what. Curitt only, on Amiro’s own calendar.
All four are prototyped (28 Oct): admin-preview (the visit link, patient widgets, settings, emails) and patient-preview (the portal). Every visit-link state, one click each: admin-preview /v (Demo). Plan: Amiro.Next/features/patient-self-serve-visits/plan.md.
Production confirms. The emails promise more.
action live for patients in production: Confirm. Cancel has an endpoint and no button; Move and Book exist nowhere on the patient side.
steps the email link asks for before anything opens: an email, then a code. Then it lands on messages, not on the visit.
patients using it yet, so we keep the emails’ promise and make it true instead of taking it back.
| Action | Production backend | Production portal | Prototype, 28 Oct |
|---|---|---|---|
| Confirm | Built | Built | Link and portal |
| Move | Staff only, BOOKED only | None | Link and portal |
| Cancel | Built refused while a payment is unrefunded | No button | Link and portal |
| Book a new visit | Public widget only | None | Portal, through the patient widget |
One visit, one link, no limits unless the clinic sets them.
requireLocallyManaged keeps external engines out.The expected step is the big button.
The same rules, signed in.
- The visit’s own step leads (Confirm, Directions, the call); a small Change or cancel opens Reschedule with a small Cancel visit.
- Reschedule scrolls the picker into view; the new time is confirmed in a sheet. Cancel is the same two-step sheet as the link.
- The clinic’s notice lands in the chat with View visit; the list re-sorts by date. Past the window, Message the clinic opens the chat with the message started.
The region’s patient widget, inside the portal.
- A focused flow. The portal’s header and tabs step aside; ✕ on the Book a visit line goes back to Visits.
- Your visit shows the patient’s own clinic from the first step, with a pencil to change it; from step 2 it folds to the total and stays on top.
- The widget’s steps: service, add-ons (nothing ticked) with the total and Continue pinned, then date and time to the visit type’s horizon (five days for Curitt), coupon above Confirm booking.
- The booking lands confirmed on the patient — no lead — and the clinic’s booking notice lands in the chat.
Patient widgets, limits and messages in Settings; the change on the calendar.
Artur builds it end to end: the backend, then both front-ends.
/v/:token outside sign-in; Reschedule, Cancel and Book a visit signed in.| Piece | What it is |
|---|---|
| Visit type | changeWindowHoursPatient, maxPatientMoves, both optional; absent means no limit. |
| Token | Random, stored hashed on the appointment; minted at booking, kept on a move, dead at the visit’s start. |
| Public API | GET /public/visit/v1/{token} · GET …/slots?date= (its own slot out) · POST …/confirm · …/move · …/cancel. Rate-limited; window and move guards; the portal switch and requireLocallyManaged. |
| Signed-in API | POST /patient/calendar/v1/appointments/{id}/move with the same guards; cancelMine as it is. |
| Patient widgets | audience, regionId, isRegionDefault (one per region, not deletable); a booking endpoint that books as the signed-in patient, no personal data or consent, no lead. |
| Messages | {{visit_link}}, {{change_link}}, {{book_link}}; RESCHEDULED / CANCELLATION from both patient paths, actor patient. |
What to settle before Curitt switches it on.
- The first reminder and a window. If a clinic sets 24 h, the 24 h reminder lands as it closes; its link line should drop once the window has closed.
- A forwarded email can change the visit. That is the trade for no sign-in; the page shows only the visit, and the link dies at its start.
- Demo times. The prototypes’ times ignore the provider’s schedule; production uses the slot engine.
- Paid visits. A cancel while a payment is not refunded is refused (409); the page should then say the clinic will contact the patient. Not prototyped.
- SMS and WhatsApp templates are not aligned: SMS is not sent; WhatsApp reminders are Carla’s. Align when either is on for a portal clinic.
- The discount in Your visit’s total: the portal shows it after the coupon, the admin widget before. Settle on one.