1
Find free slots
unavailable_dates lists days in the range with nothing free — handy for greying out a calendar without a request per day.2
Book one of them
starts_at, ends_at, practitioner_id, location_id and appointment_type_id straight from the slot — and resource_id too when the slot carries one (resource-view locations, calendar_view = 1 on GET /locations, need it; practitioner-view locations return null and need none). For a group slot send its group_master_id instead of starts_at/ends_at — you are taking a place in an existing session.3
Know what booking does — and doesn't — check
POST /appointments does not look at the practitioner’s diary. Like a staff member clicking a time in the PracticeHub calendar, it books what you send: the patient, location, practitioner, type and resource are validated, but a time that is already taken is written alongside the existing appointment, not refused. Someone may book the same time between your two calls; if that matters to you, re-check availability just before you post (or reconcile afterwards). The one capacity rule that is enforced is a group session’s size — 409 group_full when it is full. The Idempotency-Key makes a retry of the same booking safe, but a new time is a new request.Parameters
Two views of the diary
visibility=online (default)
What a patient sees when booking online. The location, appointment type and (if given) practitioner must be enabled for online booking — otherwise
422 not_online_bookable — and every online-booking rule applies: minimum advance notice, how far ahead, daily caps, online-only availability guides, slot clustering. Use this for websites, apps and marketplaces.visibility=all
What the front desk could book: every open slot for practitioners who provide the type, subject only to physical capacity (columns / resources). No online-booking rules. Use this for staff-facing tools and internal automations.
Booking is what the calendar allows staff
GET /availability is how you look before you leap; POST /appointments books what you send, exactly as a staff member clicking the calendar would — it does not check whether the practitioner is free at that time, and there is no locking between two callers. That is deliberate: a data migration or a practice that double-books on purpose needs to be able to write wherever it says. For a public flow, always book a slot /availability returned, and re-query if any time has passed since you fetched it.
Not the same as practitioner_availabilities
practitioner_availabilities is a resource: the raw working-hours records (who is in, where, when). GET /availability is a computation over those records, existing appointments, closures, resources and the appointment type — the free time that is left. Read the first to know when someone works; call the second to know when they can be booked.