Appointment $confirm | Medplum

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

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

Output

Returns 200 OK with a Bundle of all updated resources:

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