Resources
Waitlist Automation in Medplum
By Federico Ruiz Cassarino, CEO and founder, Puppeteer AI · · 6 min read
Most advice about clinic waitlists starts from the same premise: your EHR doesn't track one, so build it outside. Medplum breaks that premise. It has waitlist as a real value on the appointment resource itself, which is rare enough among EHRs that it's worth knowing exactly what it buys you and what it still leaves you to build.
The short version: Medplum removes the storage problem. It does not remove the ranking problem, and confusing the two is where a "we have a waitlist" project usually goes wrong.
A waitlist value on the appointment itself
Medplum implements the FHIR R4 appointment status valueset in full: proposed, pending, booked, arrived, checked-in, fulfilled, cancelled, noshow, entered-in-error, and waitlist.
That last value is the uncommon one. Canvas Medical runs the same spec and the same ten-value set, minus waitlist: it simply isn't an option a Canvas appointment can carry. Healthie isn't FHIR at all, and what it labels a waitlist is a single boolean on the appointment type, scoped to group sessions that hit capacity. Neither gives a patient waiting for an earlier individual slot a status of their own. Medplum does.
So among EHRs that expose scheduling at all, treating a waiting patient as first-class appointment state, rather than a tag you invented for the purpose, is close to unique to Medplum.
Where the waiting patient lives
On a system without waitlist state, a patient hoping for something sooner exists only in whatever you built to track them: a spreadsheet row, a tag, a table in your own database. The EHR has no idea. Swap integration tools and the list doesn't come with you. A staff member books that same patient from the front-desk screen and your tracking never finds out.
On Medplum, that patient is an Appointment resource with status: "waitlist". Three things follow from that.
- Anyone querying appointments sees them. Staff working directly in the Medplum UI, not just your integration, can see who is waiting.
- The clinical context travels with the record. A waitlisted appointment carries participants, a service type, a reason code, everything a booked one carries. What the patient is waiting for isn't lost.
- It outlives your tooling. The list is EHR data. Rebuild or replace your integration and the waitlist is still there.
That third point is the one worth weighing before deciding whether to build your queue on Medplum's model or next to it.
Finding who's waiting
Because waitlist is an ordinary status, finding the queue is an ordinary search: GET /fhir/R4/Appointment?status=waitlist. Everything you would normally read off a booked appointment reads the same way off a waitlisted one.
What doesn't come back is order. FHIR appointment status says a patient is waiting; it says nothing about how long, how urgently, or ahead of whom. There is no priority field and no queue position anywhere on the resource.
So Medplum hands you storage, not ranking. Building the order, how long someone has waited, what the reason code says, the patient's own history, is work you would do on any EHR, Medplum included.
Moving someone off the list: PUT or PATCH
Medplum exposes appointments at the standard FHIR paths: /fhir/R4/Appointment to create, /fhir/R4/Appointment/{id} to read and update. How you update is where the API takes a position.
A PUT replaces the entire resource. Fine when several fields change together, but it means sending the whole appointment body, and any field you leave out is gone, not left alone.
A status change by itself is better done as a PATCH carrying a JSON Patch document:
[ { "op": "replace", "path": "/status", "value": "booked" } ]
One operation, one path, one value. Nothing else on the resource moves.
That is the transition a waitlist workflow lives or dies on: a patient going from waitlist to booked the moment a slot opens. Do it as a PUT and you are reconstructing the whole appointment to change one word, silently dropping whatever field you forgot, participants and reason code included. Do it as a PATCH and that risk doesn't exist.
Cancelling works the same way: a PATCH replacing /status with cancelled, no rebuild required.
Give the record your own identifier
One field carries more weight than its size suggests. A Medplum appointment accepts an identifier array, each entry a system plus a value:
"identifier": [ { "system": "https://your-app.example/waitlist", "value": "req_8814" } ]
system is a namespace you own, value is your key. It lets an appointment carry a reference back to your side without a separate mapping table to keep in sync.
For a waitlist, that is the difference between a clean integration and a guessing game. When a waitlisted appointment turns into a booking, you need to tie that booking back to the original request: which patient, through which channel, with what promised. Without an identifier you are matching on patient plus timestamp and hoping nothing collided.
It also separates two populations that need different handling: query waitlisted appointments carrying your system's identifier and you get exactly the ones your integration created, distinct from anything staff added by hand in the UI.
Set it when you create the record. Adding it after the fact means the same guessing you built it to avoid.
What you need on your side
- A Medplum project and credentials. Medplum is open source, so this can be the hosted service or a deployment you run yourself. The API surface is identical either way.
- A decision about who creates a waitlisted appointment. Staff in the UI, patients through a request flow, or your integration. Wherever it is, that's where the ranking inputs have to be captured too, because the status alone won't carry them.
- Somewhere to hold ordering. Urgency, wait time, contact history. None of it lives on the appointment resource.
- A slot-monitoring path. So a cancellation triggers a look at the waitlist instead of sitting there unnoticed.
Honest limits
- Status isn't priority.
waitlistis a flag, not a queue. Two hundred waitlisted appointments come back with no order, and building one is the same work as on any other EHR. - Nothing expires. A waitlisted appointment stays that way until something changes it, the same rot that hits tag-based lists on other systems.
- It still has a start and end time. FHIR appointments are timed objects, so a record meaning "wants something sooner" still carries a time that may mean nothing. Pick a convention and apply it, or the reporting gets confusing fast.
- Visibility cuts both ways. Staff can see the waitlist, and staff can also book directly against a slot your automation was about to offer to someone on it. Race conditions between the UI and an integration are real.
Sources
About the author
Federico Ruiz Cassarino is the CEO and founder of Puppeteer AI, the company behind Recupra. He works with clinic operators on the scheduling and outreach problems that decide how much of a practice's booked capacity turns into revenue. Connect on LinkedIn.