Huli FHIR Core Implementation Guide (R4)
0.1.0 - Release

Huli FHIR Core Implementation Guide (R4) - Local Development build (v0.1.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

Appointment Mapping

Appointment Mapping: Huli -> FHIR R4

Resource Identity

FHIR path Cardinality Must Support Justification
Appointment.id 1..1 UUID primary key; the FHIR id is the appointment id alone
Appointment.meta.lastUpdated 0..1 Auto-updated trigger
Appointment.created 0..1 Maps to standard FHIR element

Optimistic concurrency (versioning)

Appointment supports optimistic concurrency control so concurrent editors do not silently clobber each other:

  • Every read/write response carries Appointment.meta.versionId, a weak ETag header (W/"<versionId>"), and a Last-Modified header derived from meta.lastUpdated.
  • A conditional update — PUT /fhir/R4/Appointment/{id} with an If-Match header carrying the version the client last read — is accepted only when the supplied version still matches the stored version. A stale If-Match (the resource changed since the client read it) is rejected with 409 Conflict; the client should re-read and retry against the current version.
  • An unconditional PUT (no If-Match) is still accepted — concurrency enforcement is opt-in by sending If-Match.
FHIR path Cardinality Must Support Notes
Appointment.meta.versionId 0..1 Yes Monotonic per-resource version; mirrored in the weak ETag header

Status

FHIR path Cardinality Must Support Justification
Appointment.status 1..1 Yes Required; status code

Value mapping:

Huli code FHIR code Display (ES)
proposed proposed Propuesta
pending pending Pendiente
booked booked Confirmada
arrived arrived Llegada
fulfilled fulfilled Completada
cancelled cancelled Cancelada
noshow noshow No Asistio
entered_in_error entered-in-error Error de Captura

All codes are already FHIR-aligned.

Time

FHIR path Cardinality Must Support Justification
Appointment.start 0..1 Yes Lower bound of the appointment time slot
Appointment.end 0..1 Yes Upper bound of the appointment time slot

Type and Classification

FHIR path Cardinality Must Support Justification
Appointment.appointmentType 0..1 Appointment type code

Read-only on write. Appointment.appointmentType is server-derived from the chosen serviceType and is emitted on read only. The write decoder ignores any appointmentType in the request body.

Value mapping:

Huli code SNOMED code FHIR display Display (ES)
routine 410620009 Well patient encounter Primera Vez
followup 185389009 Follow-up visit Seguimiento
procedure 308335008 Patient encounter procedure Procedimiento
emergency 373110003 Emergency encounter Urgencia
telehealth 448337001 Telemedicine encounter Telemedicina
walkin 310121005 Administrative encounter Administrativo

Specialty

FHIR path Cardinality Must Support Justification
Appointment.specialty[0] 0..* References a specialty (SNOMED coded)
Appointment.specialty[0].text 0..1 Resolved display name

Writable for multi-specialty services. On read, Appointment.specialty[0].text carries the resolved display name. On write the API decodes the chosen specialty from a specialty coding under the Huli specialty CodeSystem (system = https://fhir.huli.ai/r4/CodeSystem/specialty, code = the specialty UUID — the value a HealthcareService advertises for a multi-specialty offering). It is optional: a single-specialty (or "all") service derives the specialty server-side, so a human/SNOMED-only specialty coding is ignored. When the chosen serviceType offers two or more specialties, a specialty MUST be selected — omitting it is rejected 422 (HPB-00115), and a specialty the service does not offer is rejected 422 (HPB-00116). A present-but- malformed specialty code (non-UUID) is rejected 422 (value, Appointment.specialty).

Cancellation

FHIR path Cardinality Must Support Justification
Appointment.cancelationReason 0..1 Cancellation reason code

Value mapping:

Huli code SNOMED code FHIR text
patient_request 185332005 Solicitud del Paciente
provider_request 185333000 Solicitud del Proveedor
schedule_conflict Conflicto de Horario
emergency Emergencia
weather Clima
equipment_failure Falla de Equipo
patient_condition 185332005 Condicion del Paciente
other Otro

Text Fields

FHIR path Cardinality Must Support Justification
Appointment.description 0..1 Nullable TEXT
Appointment.patientInstruction 0..1 Nullable TEXT
Appointment.priority 0..1 INTEGER, 0=undefined

Participants

FHIR path Cardinality Must Support Justification
Appointment.participant[patient].actor 0..1 Nullable UUID (walk-ins) -> Reference(Patient)
Appointment.participant[practitioner/room/equipment] 1..* Yes The appointment's resource participants

Participant mapping:

Huli resource type FHIR actor type
practitioner Reference(Practitioner)
room Reference(Location)
equipment Reference(Device)

Room reference id-space. A room participant is emitted as Reference(Location) keyed on the room's physical-location id — the same id GET/SEARCH /Location resolves and that Schedule / HealthcareService reference — not an internal scheduling id. So the Location reference an integrator reads off an Appointment resolves via GET /Location/{id} and appears in Location search (discover → book closes through the API). On write the API resolves that Location id back to the bookable room resource; a Location that is not an active bookable room (a site/building, or a room with no bookable schedule) is rejected 400 (value).

Practitioner participant id-space. The practitioner participant is emitted as Reference(Practitioner) keyed on the PractitionerRole.id (the bookable schedulable resource that Schedule / Slot / PractitionerRole discovery surfaces) — NOT the practitioner-user id that PractitionerRole.practitioner points at. The reference type is Practitioner for FHIR conformance, but the id-space is the practitioner-role. On write, an actor referencing a practitioner-user id (rather than the role/schedulable-resource id) is not a bookable resource and is rejected 404 — discover the role, book the role.

Huli participant type FHIR required
required required
optional optional
information_only information-only
Huli participant status FHIR status
accepted accepted
declined declined
tentative tentative
needs_action needs-action

Extensions

FHIR path Cardinality Must Support Justification
Appointment.extension[confirmationStatus] 0..1 Yes Separate patient confirmation workflow

Read-only on write. The confirmation-status extension is server-derived (it tracks the separate patient-confirmation workflow) and is emitted on read only. The write decoder ignores any extension[confirmationStatus] in the request body.

Confirmation status mapping (codes the runtime emits):

Huli id FHIR extension value
1 unconfirmed
2 confirmed
3 cancelled

Administrative-meeting extensions

An administrative meeting is an appointment booked against a service whose appointment type is administrative. Administrative meetings REQUIRE a title, do NOT carry a patient participant, and MAY carry external email invitees; clinical appointments reject all three. A room (Location) participant is not required for an administrative meeting (the room requirement in the Participants table above applies to clinical/telehealth appointments only). These fields round-trip through dedicated extensions (read + write):

Extension URL (under https://fhir.huli.ai/r4/StructureDefinition/) Value Read Write Notes
huli-appointment-title valueString Yes Yes The meeting title. Required for administrative appointments (required, Appointment.extension(huli-appointment-title)); rejected on clinical appointments (value). On a PUT full-replace, omitting it clears the stored title.
huli-appointment-all-day valueBoolean Yes (emitted only when true) Yes Marks an all-day meeting. The service enforces whole-local-day (midnight-to-midnight) bounds.
huli-appointment-external-attendee nested email (valueString, required) + displayName (valueString, optional) Yes — single-resource GET / create / update only (PII; never on the search keyset path) Yes Repeats per invitee, max 30. On a PUT, supplying the extension(s) replaces the invitee set; omitting it leaves the set unchanged.

A clinical→administrative (or reverse) service flip is supported on PUT: because a FHIR PUT is a full replace, an omitted huli-appointment-title extension clears the title and an omitted Patient participant clears the patient, letting the resulting appointment satisfy the target kind's rules.

Invite emails + ICS are sent over FHIR. Creating, cancelling, or updating an administrative meeting through this API emails its huli-appointment-external-attendee invitees with a calendar (ICS) invite — the same dispatch the in-app path uses: a create sends an ICS REQUEST, a cancel sends a CANCEL, and an update diffs the attendee set (added → REQUEST, removed → CANCEL, retained → rescheduled REQUEST only when the time changed). Delivery is post-commit and at-least-once (a durable per-invitee outbox row recovered by the confirmation sweeper), so a successful write reliably notifies attendees.

Write semantics (create / update / cancel)

Public FHIR Appointment writes route through the same scheduling service the in-app calendar uses, so the public API enforces the app's scheduling rules rather than a thin direct insert. This means the request body must name the resources an appointment needs, and writes can be rejected by the same business rules the app applies.

Service is required on write

FHIR path Cardinality (write) Notes
Appointment.serviceType[0].coding[0] 1..1 system = https://fhir.huli.ai/r4/CodeSystem/org-service, code = the org service UUID. The service derives appointment type, specialty, booking policy, and per-service resource requirements. A missing or unresolvable serviceType is rejected 422 (required / value). The previous behaviour — silently defaulting to the organization's first active service — has been removed.

Participants are required and persisted

A create / field-edit must include the resource participants; they are persisted on the appointment (an earlier revision dropped them):

FHIR actor type Huli resource type Cardinality (write)
Reference(Practitioner) practitioner 1..* (at least one)
Reference(Location) room 1..1 (required)
Reference(Device) equipment 0..*
Reference(Patient) patient 0..1

The Reference(Location) room must be the discoverable physical-room Location id (the one returned by GET/SEARCH /Location); the API resolves it to the bookable room resource. A Location that is not an active bookable room is rejected 400 (value, Appointment.participant.actor).

Conflict / availability

The create and field-edit run double-booking detection and availability / calendar-scope checks across every participant resource. A slot that overlaps an existing booking for a participant is rejected with 409 Conflict — the app would have refused the same booking. Telehealth services additionally auto-provision a telemedicine session inside the same transaction.

Cancel vs. mark-in-error (status PUT)

The target status on a PUT selects the operation; the two terminal data-removal states route to different app methods with different permissions:

FHIR status App operation Permission Guards
cancelled cancel appointments.delete Requires a cancelationReason that resolves to one of the org's active cancellation reasons (by coding code or SNOMED coding); blocked 409 when a clinical encounter is linked to the appointment; comment maps to the cancellation note.
entered-in-error status → entered-in-error appointments.edit A data-entry correction, not a cancellation; no reason required.
anything else reschedule / edit appointments.edit Re-runs conflict/availability/scope checks.

This closes the prior drift where a PUT status=cancelled bypassed the cancel permission, the required-reason check, and the linked-encounter block.

Side effects

A successful create / cancel / edit updates open staff calendars live and records an audit-trail entry. (Reminder and confirmation-email dispatch are tracked as a follow-up — every scheduling guard, the audit trail, and the live calendar update ship on the FHIR path today.)

Search parameters

GET /Appointment supports the following search parameters (cursor-paginated; see the CapabilityStatement):

Parameter Type Filters on
_id token The appointment UUID.
patient reference Patient/{id}.
practitioner reference Practitioner/{id} (the schedulable-resource id emitted on read).
date date The appointment start (prefix-aware: eq/gt/ge/lt/le).
status token The FHIR appointment status code.
appointment-type token The service's appointment-type code (e.g. administrative, routine) — lets a client list only administrative meetings or only clinical bookings.

The keyset search path emits the huli-appointment-title / huli-appointment-all-day extensions but NEVER the huli-appointment-external-attendee extensions — external invitee emails are PII and surface only on the single-resource GET/create/update responses.

Fields NOT Mapped (Huli-internal)

Huli concept Reason
Operational notes Not exposed (D-008); staff-only operational notes are never surfaced through this profile
Soft-delete status Not exposed; internal lifecycle
Recurrence pattern Not exposed; no FHIR R4 recurrence model
Recurrence exception flag Not exposed; scheduling internal
Recurrence original start Not exposed; scheduling internal
Reschedule count Not exposed; analytics metric
Custom color Not exposed; pure UI concern
Overbook flag Not exposed; operational detail
Courtesy flag Not exposed; billing detail
Advance-request flag Not exposed; workflow detail
Insurance snapshot Carried by the HuliInsuranceSnapshot Appointment extension (see Extensions)
Attachments Not modelled through FHIR in this IG
Custom fields Unstructured; no FHIR mapping
Created-by Audit metadata (emitted as the huli-created-by extension)
Modified-by Audit metadata (emitted as the huli-modified-by extension)
Cancelled-by Audit metadata (emitted in the huli-cancellation-info extension)
Cancelled-on Audit metadata (emitted in the huli-cancellation-info extension)
Cancellation notes Not exposed; operational detail
New-patient flag Not exposed; computed flag
Warnings Not exposed; runtime computed
Location Not exposed; derived from the participant's schedule
Telemedicine Not modelled through FHIR in this IG
Tags Not modelled through FHIR in this IG