Appointment $confirm
Alpha
The $confirm operation is currently in alpha.
The $confirm operation confirms a held Appointment by atomically setting its status to booked and upgrading all busy-tentative Slot resources it references to busy in a single FHIR transaction.
Booking Lifecycle
$confirm is the final step in a hold-then-book flow:
$find— Query available time slots$hold— Reserve a slot. Creates apendingAppointment andbusy-tentativeSlots$confirm— Confirm the hold. Transitions the Appointment tobookedand Slots tobusy
Use Cases
- Patient initiated booking: A patient can self-schedule a "pending" appointment; staff confirms when accepting it
- Staff approval workflow: A coordinator places a hold on behalf of a patient; a clinician or admin confirms
- Automated confirmation: Programmatically confirm a hold after an external step (e.g., payment or consent) completes
Invoke the $confirm operation
[base]/R4/Appointment/:id/$confirm
- TypeScript
- cURL
import { MedplumClient } from '@medplum/core';
import type { Bundle } from '@medplum/fhirtypes';
const medplum = new MedplumClient();
const bundle = await medplum.post<Bundle>(
medplum.fhirUrl('Appointment', 'my-appointment-id', '$confirm')
);
curl -X POST 'https://api.medplum.com/fhir/R4/Appointment/my-appointment-id/$confirm' \
-H "Authorization: Bearer MY_ACCESS_TOKEN"
Parameters
This operation takes no input parameters. The appointment to confirm is identified by the id in the URL.
Constraints
- The Appointment must have
status: pendingorstatus: proposed. All other statuses are rejected with HTTP 409 Conflict. - All
Slotresources referenced byAppointment.slotmust exist and be readable by the caller.
Output
Returns 200 OK with a Bundle of all updated resources:
- One
Appointmentwithstatus: booked - One
Slotper referenced slot that wasbusy-tentative(nowbusy). Slots already inbusystatus are returned unchanged.
Example Response
{
"resourceType": "Bundle",
"type": "transaction-response",
"entry": [
{
"resource": {
"resourceType": "Appointment",
"id": "my-appointment-id",
"status": "booked",
"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" }
],
"slot": [{ "reference": "Slot/my-slot-id" }]
}
},
{
"resource": {
"resourceType": "Slot",
"id": "my-slot-id",
"status": "busy",
"start": "2026-03-10T09:00:00.000Z",
"end": "2026-03-10T10:00:00.000Z",
"schedule": { "reference": "Schedule/dr-smith-schedule" }
}
}
]
}
Confirmation Logic
$confirm performs the following steps atomically inside a database transaction:
- Reads the Appointment identified by the URL
id - Validates that the Appointment's
statusispendingorproposed - Loads all
Slotresources listed inAppointment.slot - Updates any
busy-tentativeSlots tobusy - Sets the Appointment's
statustobookedand saves it - Returns the updated Appointment and Slots in a Bundle
The transaction uses serializable isolation for safety when there are concurrent scheduling operations affecting the same appointment.
Error Responses
Appointment Not Found
{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "not-found", "details": { "text": "Not found" } }]
}
HTTP status: 404
Appointment Not in Confirmable State
{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "conflict", "details": { "text": "Appointment cannot be confirmed in 'booked' status" } }]
}
HTTP status: 409
Referenced Slot Not Found
{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Loading slots failed" } }]
}
HTTP status: 400
Related
- Appointment
$hold- Place a hold on a slot (the preceding step) - Appointment
$cancel- Cancel an Appointment - Appointment
$find- Find available slots - Scheduling Overview - High-level scheduling concepts
AppointmentresourceSlotresource