Skip to main content

Appointment $confirm

Beta

The $confirm operation is currently in beta.

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, ensuring safety when concurrent scheduling requests are received.

  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

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