Skip to content

FHIR → Oscar Mapping (Schedule + Slot + Appointment)

This page describes how FHIR-side writes (booking actions taken against the FHIR server) flow back to OscarPro. The structured mapping is in fhir-to-oscar.yaml; this page narrates the write paths + their limitations.

TL;DR — most of Schedule/Slot is read-only

Schedule and Slot resources are materialised by the Oscar-side converter from templates and appointment data. They are not first-class records in OscarPro. As a result, most fields are read-only from the FHIR side — the source system of record is OscarPro, and the converter is the sole writer.

The exceptions are:

  • Slot.status — booking system PATCHes to manage the busy/busy-tentative/free lifecycle
  • Slot.overbooked — booking system sets on intentional overbook
  • Appointment.* — full write path (this is the primary booking flow)

The write paths that matter

1. Booking an appointment from a free Slot

POST /Appointment
{
  "resourceType": "Appointment",
  "status": "booked",
  "slot": [{ "reference": "Slot/42-20260601-36" }],
  "start": "2026-06-01T09:00:00-04:00",
  "end":   "2026-06-01T09:15:00-04:00",
  "participant": [ ... Patient, PractitionerRole, Location ... ]
}

What happens behind the scenes:

  1. The FHIR server creates the Appointment resource
  2. The Oscar-side write-back process inserts a row into the OscarPro appointment table (start_time, end_time, provider_no, demographic_no, etc.)
  3. The booking client typically follows up with PATCH Slot.status = busy (next section)
  4. On the next converter re-emission, Slot.status is recomputed from the appointment-overlay logic — it'll show busy based on the new row, regardless of whether the booking system PATCHed it

Note: the Appointment.slot linkage is not persisted back to OscarPro in Phase 1. OscarPro's appointment table has no Slot reference column; the linkage exists only in the FHIR layer. This is acceptable because the linkage can be reconstructed at re-emission time from the overlay logic.

2. Slot status PATCH lifecycle

The booking system PATCHes Slot.status as part of the booking flow:

When PATCH Notes
User starts a hold Slot.status = busy-tentative Booking-system-only; converter never writes this value
User confirms Slot.status = busy + POST Appointment Effectively recomputed on next converter run too
User abandons / hold times out Slot.status = free (via reconciler job) Booking-system-owned
Appointment cancelled Slot.status = free (or wait for converter re-emission) Either path works

These PATCHes live only in the FHIR layer between converter re-emissions. OscarPro has no busy-tentative analogue.

3. Cancelling an Appointment

PATCH /Appointment/<id>
{ "op": "replace", "path": "/status", "value": "cancelled" }

What happens:

  1. Appointment.status flips to cancelled in the FHIR server
  2. Oscar-side write-back updates the appointment row's status flag (e.g. 'C')
  3. On the next converter re-emission, the appointment-overlay logic no longer treats this slot as busy → Slot.status reverts to free

What you CANNOT do

Action Why
Edit Slot.start / Slot.end Slot windows are fixed by the template. Change the template in OscarPro instead.
Edit Schedule.actor Provider/location assignment is a source-system concern (Phase 1).
Create a Slot directly via the FHIR server Slots are materialised; the converter is the sole writer. Creating one manually will be overwritten on next re-emission.
Persist Slot.status = busy-tentative durably The converter doesn't write this status, and OscarPro has no analogue. If a booking system needs hold state to survive converter re-emissions, store it out-of-band.

Concurrency

The main race condition is converter re-emission vs booking-system status writes. Mitigation:

  • Booking system SHOULD use If-Match against the Slot's ETag before PATCHing
  • If the PATCH conflicts (the converter overwrote), the booking system re-reads and retries
  • Phase 1 does NOT enforce optimistic concurrency at the profile level — it's a recommended practice, not a profile invariant

Worked examples