Resources
Filling Cancelled Appointments in Avon Health
By Ian Spektor, CTO, Puppeteer AI · · 6 min read
Avon Health's API reads like a modern developer product rather than a healthcare system, and that isn't only cosmetic. Two of its design choices change what you can build on top of it when a slot opens up.
This is a field-level walkthrough of both, and what they mean for filling a cancelled slot.
Availability, and the words it uses
Open time comes from a slot list:
GET /v2/slots?appointment_type={id}&search_from={iso8601}&search_until={iso8601}&providers={id}
Three parameters are required: appointment_type, search_from and search_until. Two are optional filters: patient, and providers, which is an array of provider ids rather than a single value. You still don't have to resolve a schedule or a location first, which is less ceremony than most scheduling APIs ask for.
The date bounds are search_from and search_until rather than the start / end most systems in this series use. Both are ISO 8601 timestamps, and because they are required, a request that sends the usual names gets an error back rather than a result set.
Everything comes back in an envelope
Responses aren't bare arrays. A list looks like this:
{ "object": "list", "data": [ ... ] }
The object key names the shape and data holds the payload. Familiar if you've worked with a payments API that does the same thing; less ceremony than a FHIR bundle, and no per-entry resource nesting.
Worth handling defensively too. A response where object isn't "list" isn't a list, and treating it as one produces confusing failures downstream rather than a clean error at the boundary.
Appointment types are objects you can create
This is the first of two genuinely unusual things here.
On most EHRs, appointment types are configuration. Someone sets them up in an admin screen, you look up their ids, and your integration references them. Avon exposes them as a full CRUD resource: you can list, read, create, update and delete them through the API, though a type already used by an appointment can be neither updated nor deleted. Creating one is a single call:
POST /v2/appointment_types
with eight required fields, name, duration, group, max_participants, charge_while_booking, charge_after_appointment, settings and created_by, and an optional description. The reference's own cURL example sends only five of the eight, so test against the API rather than the sample.
The reason this matters for cancellations: when a slot opens, the visit type that was cancelled isn't always the visit type best suited to filling it. A cancelled 60-minute intake leaves an hour that might be better used as two 30-minute follow-ups. On most systems that's a conversation with whoever administers the EHR. Here it's an API call.
That's a capability worth using carefully rather than enthusiastically. Appointment types created programmatically are still real configuration that staff will see and have to make sense of, and an integration that quietly generates types will produce a settings screen nobody can read within a few months. Create deliberately, name them for humans, and prefer reusing an existing type over minting a new one.
Group visits are first-class
The second unusual thing is on that same resource: group and max_participants.
An appointment type can be marked as a group type with a participant ceiling. The EHR understands that a single slot holds several patients, rather than you simulating it with overlapping bookings.
For cancellation work this opens a path that doesn't exist elsewhere in this series. When someone drops out of a group session, the slot isn't empty. It's under-filled. The distinction changes the outreach entirely: the session runs regardless, so there's no urgency to protect revenue, but there's still a seat and someone who'd benefit from it. Different message, different timeline, and a much lower cost of not filling it.
It also means your availability logic can't assume one slot equals one patient. The documented slot object carries no capacity or seat count, so remaining seats have to be worked out on your side: the attendees already on the group appointment, against the type's max_participants.
Practices running shared medical appointments, group therapy, or class-based programs get this modeled properly rather than approximated. Practices that don't run groups can ignore it entirely, but availability code that assumes single-patient slots will need revisiting the day one starts.
Appointments read the same way slots do
Reading existing bookings uses the same list shape as everything else:
GET /v2/appointments
with filters narrowing the set, and the response arriving in the same { "object": "list", "data": [...] } envelope.
The consistency is worth pointing out because it is not the norm. On FHIR systems, slots come back as a bundle of Slot resources and appointments as a bundle of Appointment resources, each with its own nesting and its own search-parameter conventions to learn. Here the two are shaped alike, and a client that handles one handles the other.
That shortens the path from being able to list availability to having a working cancellation workflow. Most of the integration cost on scheduling APIs isn't the logic. It's the accumulated small differences between adjacent endpoints, each one a place to get something subtly wrong. Fewer differences means fewer of those places.
It also makes the local mirror easier. Any workflow that reacts quickly to cancellations ends up keeping its own copy of upcoming appointments rather than querying the EHR on every decision, and a uniform response shape makes that copy considerably less code to maintain.
What a cancellation workflow looks like
String the pieces above together and a cancellation workflow looks like this:
- Notice the cancellation. However that reaches you today, a webhook or a poll.
- Re-query availability. Search
/v2/slotsagain for the affected provider and appointment type, bounded withsearch_fromandsearch_until. - Check what actually freed up. A whole slot, or a seat in a group session. The two warrant different outreach.
- Rank the patients who could take it. Avon knows what's free. It doesn't know who's waiting for it, so that list is built and held outside the EHR, same as everywhere else in this series.
- Book. A slot has no id to book against, so a new visit is a
POST /v2/appointmentswith the appointment type, start and end time, host and attendees. A seat in an existing group session is aPOST /v2/appointments/:idthat adds the patient as a new attendee.
The check step is the one specific to Avon, and it's the one that gets skipped when the integration is ported from a system without group support. A message telling someone their appointment slot is available reads as wrong when what actually opened up is one seat in a group session.
What you need on your side
- API credentials for your Avon Health organization. Scoped per organization, the same as everywhere else in this series.
- Appointment type ids, or a plan to create them. Decide upfront whether you mint types programmatically, and if so, agree on a naming convention before the first one exists.
- A way to tell group types from single-patient types. Availability means something different for each.
- The ranked patient list. It lives outside the EHR here, as it does on every system in this series.
Honest limits
- No waitlist concept. Avon's scheduling surface covers slots, appointments and appointment types. Patients wanting an earlier visit aren't represented, so that list is yours to hold and keep clean.
- Slot availability reflects configuration. If provider availability or appointment type duration isn't set up correctly, the slots you get back won't match what staff see.
- Group capacity needs its own handling. Remaining seats is a different question from slot availability, and code that conflates them will offer seats in sessions that are already full.
- Programmatic appointment types are easy to abuse. The capability is genuinely useful, and the failure mode, a settings screen nobody owns anymore, is slow and hard to reverse.
The same guide for other EHRs: filling cancelled appointments in Healthie, filling cancelled appointments in Canvas Medical, and filling cancelled appointments in SimplePractice.
Sources
About the author

Ian Spektor is the CTO of Puppeteer AI, the company behind Recupra. He leads engineering and technology across the company, building the AI agents, integrations and infrastructure that let healthcare teams reach their patients at scale, securely and in line with HIPAA. More from Ian · LinkedIn