- Discover what can be booked: the clinic’s services and clinicians.
- Check the free times for a service.
- Book one, for a patient you already have (see Patients).
- Change it later: confirm it, move it, or cancel it. Nothing is ever deleted.
Who can use it
Appointments use three permissions. See Permissions for how a key gets them.
The two patient-information permissions need the API add-on and the accepted agreement, and for an organization the
approval for patient data. Read Patient data access first. A test key (
ehr_test_…)
reaches only your workspace’s sandbox, with invented patients and services, so you can try everything below safely.
An appointment for a patient the clinic has restricted never appears in a list. A restricted patient’s appointment, one that does not exist, and one in another clinic all answer 404 with
code: "not_found": they are never told apart. See Errors.
What an appointment looks like
id and status are always present. The fields you will use most:
Fields about the clinic’s internal scheduling and AI scoring (for example
ai_category and predicted_show_probability) are returned for information only and cannot be written.
See bookable services
The things a patient can book, such as a consultation. Use a service’sid to find times and to book.
GET /v1/clinics/{clinic_id}/appointments/services
See clinicians
The clinic’s clinicians, optionally only those who offer one service.GET /v1/clinics/{clinic_id}/appointments/clinicians
Find free times
The times a service can be booked over a range of dates.GET /v1/clinics/{clinic_id}/appointments/availability
date and time are in the clinic’s timezone, which comes back beside the list. A free time is a hint, not a
hold. Someone else can take it before you book, so always be ready for a 409 when you book (see
Things to know).
List appointments
GET /v1/clinics/{clinic_id}/appointments
Book an appointment
Books a patient for a service at a time. Send anIdempotency-Key header or the request is refused, so that a
retry never books twice. See Retries and duplicates.
POST /v1/clinics/{clinic_id}/appointments
A field that is not listed is refused with
invalid_request. Not supported through the API: a visit that bundles
several services, redeeming a package, deposit or discount, attaching default documents, and group appointments. Book one patient, one service at a time.
The answer is the appointment:
Read one appointment
GET /v1/clinics/{clinic_id}/appointments/{appointment_id}
Change or confirm an appointment
Moves an appointment, changes its clinician, venue, urgency, notes or reminder settings, or confirms it. Send only what changes. Send anIdempotency-Key.
PATCH /v1/clinics/{clinic_id}/appointments/{appointment_id}
You cannot change
patient_id, service_id, location_id or any payment field after booking. Sending one is
refused with invalid_request, as is any other field not listed. To book a different patient or service, cancel and book again.
An appointment that is already cancelled, completed or no_show can no longer be changed.
With a test key, changing start_at is not supported in the sandbox: cancel and book again.
The answer is the updated appointment, shaped as in Read one appointment.
Cancel an appointment
POST /v1/clinics/{clinic_id}/appointments/{appointment_id}/cancel
This is the only way to set an appointment to cancelled. It does not delete anything: the appointment stays on
record with its reason. The body is optional. Send an Idempotency-Key.
status still cancelled. An appointment that is completed or no_show cannot be cancelled.
Things to know
Refusals you may meet
Every error has the shape described in Errors.
Idempotency. Book, change and cancel all need an
Idempotency-Key. Use a fresh one per action and the same one
for a retry. The three read routes and the discovery routes need none.
Pagination. Only the list of appointments is paged (limit, starting_after, has_more, next_cursor). The
services, clinicians and availability lists return everything in one answer. See Pagination.
Dates and time zones. You send start_at with an offset. Free times and each appointment’s timezone are in the clinic’s own time zone.
FHIR. Add Accept: application/fhir+json to the list or the single read and you get an Appointment (or a
Bundle of them). You can also book (POST) and change (PATCH) by sending a FHIR Appointment with
Content-Type: application/fhir+json; the appointment must reference the clinic’s service as a HealthcareService
in basedOn. The answer to a write is still our JSON shape. The discovery routes and the cancel route have no FHIR
form. See FHIR R4.
Organization keys and connections. An organization key reaches only the clinics it is connected to. See
Connections.
Common tasks
Book a patient’s next visitGET …/appointments/servicesand choose a service.GET …/appointments/availability?service_id=…&from=…&to=…and choose a time.POST …/appointmentswith the patient, service, time and a freshIdempotency-Key. On409 slot_taken, go back to step 2.
- Find the patient with Search by identifiers.
GET …/appointments?patient_id=PATIENT_ID&from=2026-10-01&to=2026-12-31.
GET …/appointments/cliniciansfor the clinician’sid.GET …/appointments?clinician_id=…&from=2026-10-12&to=2026-10-12.
PATCH …/appointments/APPOINTMENT_IDwith the newstart_atand a freshIdempotency-Key. A409means the new time is not free.
POST …/appointments/APPOINTMENT_ID/cancelwith an optionalreason.
- Page through
GET …/appointments?updated_since=LAST_TIMEuntilhas_moreisfalse, and store the time you started.