Skip to main content

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:

  1. $find — Query available time slots
  2. $hold — Reserve a slot. Creates a pending Appointment and busy-tentative Slots
  3. $confirm — Confirm the hold. Transitions the Appointment to booked and Slots to busy

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
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')
);

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: pending or status: proposed. All other statuses are rejected with HTTP 409 Conflict.
  • All Slot resources referenced by Appointment.slot must exist and be readable by the caller.

Output

Returns 200 OK with a Bundle of all updated resources:

  • One Appointment with status: booked
  • One Slot per referenced slot that was busy-tentative (now busy). Slots already in busy status 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:

  1. Reads the Appointment identified by the URL id
  2. Validates that the Appointment's status is pending or proposed
  3. Loads all Slot resources listed in Appointment.slot
  4. Updates any busy-tentative Slots to busy
  5. Sets the Appointment's status to booked and saves it
  6. 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