Appointment — Booking of a healthcare event
The FHIR scheduling building block: medical appointments, physical therapy sessions, telehealth visits, operating-room bookings. It binds patient, practitioner, location and time slot into a single resource.
Purpose of the resource
Appointment represents the booking of a healthcare event for one or
more patients, practitioners, RelatedPersons and/or devices at a specific date and
time. It accommodates both clinical scheduling and community-style appointment systems
(walk-in clinics, follow-up calls) and supports recurring appointments via
recurrenceTemplate.
The resource is purposely orthogonal to Schedule
(the practitioner's availability calendar) and Slot
(a time window inside that calendar). The canonical pattern: a Schedule exposes
free Slots, the client app creates an Appointment that references the
consumed Slot(s), and those Slot(s) flip to busy.
Key fields
The R5 StructureDefinition publishes 32 top-level elements. The most relevant in practice:
| Field | Type | Cardinality | Role |
|---|---|---|---|
identifier | Identifier[] | 0..* | External identifiers of the appointment (EHR business number, voucher, patient barcode). |
status | code | 1..1 | Lifecycle status. Modifier — changes the meaning of the appointment. |
cancellationReason | CodeableConcept | 0..1 | Cancellation reason. Only present when status=cancelled or noshow. |
class | CodeableConcept[] | 0..* | Appointment classification (ambulatory, inpatient, emergency…). |
serviceCategory | CodeableConcept[] | 0..* | Broad category (General Practice, Dental, Imaging…). |
serviceType | CodeableReference[] | 0..* | Specific service (tooth extraction, brain MRI…). |
specialty | CodeableConcept[] | 0..* | Practitioner specialty (SNOMED CT 394814009 = General practice). |
appointmentType | CodeableConcept | 0..1 | Functional type (FOLLOWUP, ROUTINE, WALKIN, EMERGENCY… — v2-0276). |
reason | CodeableReference[] | 0..* | Clinical reason (may reference a Condition or an Observation). |
priority | CodeableConcept | 0..1 | Priority (R = Routine, EM = Emergency… — v3-ActPriority). |
start | instant | 0..1 | Effective start datetime. instant = second precision + mandatory timezone. |
end | instant | 0..1 | End datetime. |
minutesDuration | positiveInt | 0..1 | Duration in minutes (redundant with start/end but useful for not-yet-positioned appointments). |
slot | Reference(Slot)[] | 0..* | The Slot(s) consumed by this appointment (a long appointment can consume multiple consecutive Slots). |
requestedPeriod | Period[] | 0..* | Requested time window when the appointment is in proposed but not yet positioned. |
subject | Reference(Patient | Group) | 0..1 | Primary subject — typically the patient. |
participant | BackboneElement[] | 1..* | All actors involved — patient, practitioner, location, device. Each participant carries actor, required, status. |
basedOn | Reference(CarePlan | DeviceRequest | MedicationRequest | ServiceRequest)[] | 0..* | The appointment is created in execution of a prior care request. |
recurrenceTemplate | BackboneElement[] | 0..* | Recurrence definition (weekly, monthly…) using an RRULE-like rule. |
virtualService | VirtualServiceDetail[] | 0..* | Video-conferencing details (URL, service type, access code) for telehealth. |
patientInstruction | CodeableReference[] | 0..* | Patient-facing instructions (fast before, bring prescription, arrive 10 min ahead…). |
Lifecycle statuses
The status code is mandatory and carries the appointment's business
meaning. HL7 publishes the following values (Required binding):
| Code | Meaning |
|---|---|
proposed | Appointment suggested but no date set. Awaiting confirmation by participants. |
pending | Appointment positioned, awaiting acceptance by all participants. |
booked | Confirmed. Slot locked. |
arrived | Patient has arrived (check-in to the waiting room). |
fulfilled | Appointment took place (the associated Encounter is usually already created). |
cancelled | Cancelled. cancellationReason carries the reason. |
noshow | Patient did not show up. Distinct from cancelled for reporting purposes. |
entered-in-error | Erroneous entry — fix upstream. |
checked-in | Front-desk check-in (alternative to arrived, used by IT workflows). |
waitlist | Wait list — patient will be contacted on cancellation. |
JSON example
A 20-minute follow-up appointment in a GP practice's system, on 22 May 2026 at 09:00 Paris time, between Dr Adam Careful and patient Peter Chalmers, in the "South Wing, second floor" location.
{
"resourceType": "Appointment",
"id": "example",
"status": "booked",
"serviceCategory": [{
"coding": [{
"system": "http://example.org/service-category",
"code": "gp",
"display": "General Practice"
}]
}],
"serviceType": [{
"concept": {
"coding": [{
"system": "http://example.org/service-type",
"code": "52",
"display": "General Discussion"
}]
}
}],
"specialty": [{
"coding": [{
"system": "http://snomed.info/sct",
"code": "394814009",
"display": "General practice"
}]
}],
"appointmentType": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v2-0276",
"code": "FOLLOWUP",
"display": "A follow up visit from a previous appointment"
}]
},
"reason": [{
"concept": {
"coding": [{
"system": "http://snomed.info/sct",
"code": "413095006",
"display": "Clinical mental health assessment"
}]
}
}],
"priority": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v3-ActPriority",
"code": "R",
"display": "Routine"
}]
},
"description": "Follow-up on last month's blood test results",
"start": "2026-05-22T09:00:00+02:00",
"end": "2026-05-22T09:20:00+02:00",
"minutesDuration": 20,
"slot": [{ "reference": "Slot/example" }],
"created": "2026-05-14T10:30:00+02:00",
"patientInstruction": [{
"concept": {
"text": "Please arrive 10 minutes ahead of your appointment time"
}
}],
"participant": [{
"actor": { "reference": "Patient/example", "display": "Peter James Chalmers" },
"required": true,
"status": "accepted"
}, {
"type": [{
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
"code": "ATND"
}]
}],
"actor": { "reference": "Practitioner/example", "display": "Dr Adam Careful" },
"required": true,
"status": "accepted"
}, {
"actor": { "reference": "Location/1", "display": "South Wing, second floor" },
"required": true,
"status": "accepted"
}]
} Worth pointing out in this example:
- The
participant[]array lists three actors: patient, practitioner, location. Each has its ownstatus(accepted/declined/tentative). slotreferences a Slot resource; that Slot must flip tobusyon the practitioner's Schedule.startandendareinstantwith timezone: essential for multi-timezone apps.patientInstructioncarries free text shown to the patient.
Booking cycle
The canonical FHIR booking pattern combines three resources and two standard operations:
- The client app queries
GET /Slot?schedule=Schedule/123&status=free&start=ge2026-05-22to fetch the practitioner's free Slots. - The user picks a slot; the app posts an Appointment via
POST /Appointmentwithstatus=pendingand aslotreference. - The server validates availability, flips the referenced Slot to
status=busy, and flips the Appointment tostatus=bookedonce all requiredparticipant[].statusvalues areaccepted. - The server may expose a
$findor$bookoperation as a convenience that bundles those steps (see US Core and Argonaut Scheduling profiles). - When the patient arrives, the EHR sets the status to
arrivedorchecked-in. Once the encounter has taken place, the status flips tofulfilledand an Encounter resource is created.
Common pitfalls
- Slot not locked — creating an Appointment without flipping the referenced Slot(s) to
busyallows double-booking. This synchronization is the server's responsibility, not the client's. - start/end without timezone — the FHIR
instanttype requires an offset. Dropping the timezone (2026-05-22T09:00:00without suffix) is rejected by the validator. participant[].required=truewithstatus=declined— business inconsistency: a required participant that has declined can't leave the Appointment inbooked.- Telehealth without
virtualService— a video-mode Appointment must carry at least onevirtualServiceentry withchannelTypeand connection URL, otherwise the patient has no technical way to join the call. - Inconsistent
recurrenceTemplate— a recurring template must describe occurrences reachable withinrequestedPeriod. An RRULE pointing outside that range is silently ignored by some servers. - Confusion
cancelled/noshow—cancelled= explicit cancellation before the appointment.noshow= patient did not come and did not cancel. They count differently in reporting and billing.
Related resources
- Schedule — the availability calendar of a practitioner, device or location.
- Slot — each free/busy slot exposed by a Schedule.
- AppointmentResponse — asynchronous response from a participant to an invitation.
- Encounter — the healthcare interaction created from the appointment once the patient is present.
- Patient, Practitioner — the main actors referenced from
participant. - ServiceRequest, CarePlan, DeviceRequest — care orders that may trigger the Appointment via
basedOn.
See also: the FHIR R5 index for the full resource list.