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 400 Bad Request.
  • 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 any HealthcareService referenced by Appointment.serviceType and validates that it is not inactive
  4. Loads all Slot resources listed in Appointment.slot
  5. Updates any busy-tentative Slots to busy
  6. Sets the Appointment's status to booked and saves it
  7. 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

HealthcareService Inactive​

A HealthcareService with active: false cannot be scheduled against. Appointments already booked for the service can still be canceled.

{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "invalid", "details": { "text": "HealthcareService is inactive" } }]
}

HTTP status: 400

Referenced HealthcareService Not Found​

Returned when a HealthcareService referenced by Appointment.serviceType has been deleted, or is not readable by the caller's access policy.

{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Loading HealthcareService failed" } }]
}

HTTP status: 400

Referenced Slot Not Found​

{
"resourceType": "OperationOutcome",
"issue": [{ "severity": "error", "code": "invalid", "details": { "text": "Loading slots failed" } }]
}

HTTP status: 400