Appointment $cancel
The $cancel operation is currently in beta.
The $cancel operation cancels an Appointment by atomically setting its status to cancelled and deleting all Slot resources it references in a single FHIR transaction. An optional cancelationReason records why the Appointment was canceled.
Use Cases
- Patient-initiated cancellation: Cancel a scheduled appointment at the patient's request and free the provider's time
- Staff-initiated cancellation: Cancel an appointment from an admin or scheduling workflow
- Automated cancellation: Programmatically cancel appointments based on external triggers (e.g., provider unavailability, EHR integration)
Invoke the $cancel operation
[base]/R4/Appointment/:id/$cancel
- TypeScript
- cURL
import { MedplumClient } from '@medplum/core';
import type { Appointment, Parameters } from '@medplum/fhirtypes';
const medplum = new MedplumClient();
const appointment = await medplum.post<Appointment>(
medplum.fhirUrl('Appointment', 'my-appointment-id', '$cancel')
);
// Optionally, record why the Appointment was canceled
const appointmentWithReason = await medplum.post<Appointment>(
medplum.fhirUrl('Appointment', 'my-appointment-id', '$cancel'),
{
resourceType: 'Parameters',
parameter: [
{
name: 'cancelationReason',
valueCodeableConcept: {
coding: [
{
system: 'http://terminology.hl7.org/CodeSystem/appointment-cancellation-reason',
code: 'pat-cpp',
display: 'Patient: Canceled via Patient Portal',
},
],
},
},
],
} satisfies Parameters
);
curl -X POST 'https://api.medplum.com/fhir/R4/Appointment/my-appointment-id/$cancel' \
-H "Authorization: Bearer MY_ACCESS_TOKEN"
Optionally, record why the Appointment was canceled:
curl -X POST 'https://api.medplum.com/fhir/R4/Appointment/my-appointment-id/$cancel' \
-H "Authorization: Bearer MY_ACCESS_TOKEN" \
-H "Content-Type: application/fhir+json" \
-d '{
"resourceType": "Parameters",
"parameter": [
{
"name": "cancelationReason",
"valueCodeableConcept": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/appointment-cancellation-reason",
"code": "pat-cpp",
"display": "Patient: Canceled via Patient Portal"
}
]
}
}
]
}'
Parameters
The appointment to cancel is identified by the id in the URL.
| Name | Type | Description | Required |
|---|---|---|---|
cancelationReason | CodeableConcept | The coded reason the Appointment was canceled. Stored on Appointment.cancelationReason. | No |
Cancelation Reason
When cancelationReason is provided, it is written to Appointment.cancelationReason on the canceled Appointment. When it is omitted, the field is left untouched.
FHIR R4 spells this element cancelationReason, with a single l. The operation parameter uses the same spelling as the resource element.
FHIR recommends coding this with the appointment-cancellation-reason CodeSystem, but any CodeableConcept is accepted.
{
"resourceType": "Parameters",
"parameter": [
{
"name": "cancelationReason",
"valueCodeableConcept": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/appointment-cancellation-reason",
"code": "pat-cpp",
"display": "Patient: Canceled via Patient Portal"
}
]
}
}
]
}
Constraints
- The Appointment must have
status: bookedorstatus: pending. All other statuses are rejected. - All
Slotresources referenced byAppointment.slotmust exist and be readable by the caller.
Output
Returns 200 OK with the updated Appointment resource directly:
- One
Appointmentwithstatus: cancelled, andcancelationReasonset if it was provided
All Slot resources that were referenced by the Appointment are deleted and do not appear in the response.
Example Response
{
"resourceType": "Appointment",
"id": "my-appointment-id",
"status": "cancelled",
"cancelationReason": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/appointment-cancellation-reason",
"code": "pat-cpp",
"display": "Patient: Canceled via Patient Portal"
}
]
},
"start": "2026-03-10T09:00:00.000Z",
"end": "2026-03-10T10:00:00.000Z",
"participant": [
{ "actor": { "reference": "Practitioner/dr-smith" }, "status": "tentative" },
{ "actor": { "reference": "Patient/my-patient-id" }, "status": "accepted" }
]
}
Cancellation Logic
$cancel performs the following steps atomically inside a database transaction, ensuring safety when concurrent scheduling requests are received.
- Reads the Appointment identified by the URL
id - Loads all
Slotresources listed inAppointment.slot - Validates that the Appointment's
statusisbookedorpending - Sets the Appointment's
statustocancelled, along withcancelationReasonif one was provided, and saves it - Deletes all referenced Slots
- Returns the updated Appointment
Error Responses
Appointment Not Found
{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "not-found", "details": { "text": "Not found" } }]
}
Appointment Not in Cancelable State
{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Appointment cannot be canceled in 'arrived' status" } }]
}
Referenced Slot Not Found
{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Loading slots failed" } }]
}
Related
- Appointment
$book- Book an Appointment (the inverse operation) - Appointment
$find- Find available slots - Scheduling Overview - High-level scheduling concepts
AppointmentresourceSlotresource