Appointment (FHIR R4)

FHIR You're viewing the FHIR R4 reference.

A booking of a healthcare event between patient(s) and practitioner(s) for a specific date/time. Maps to HuliPractice appointment slots with start/end times, status, and participant references.

This reference is generated from the canonical Huli Public API OpenAPI specification and the server's FHIR R4 CapabilityStatement — do not edit it directly.

FHIR R4 Specification

Official spec: Appointment — HL7 FHIR R4

Supported Interactions

  • read — Read a single resource by ID (GET /fhir/R4/{Resource}/{id})
  • search-type — Search resources with query parameters (GET /fhir/R4/{Resource}?...)
  • create — Create a new resource (POST /fhir/R4/{Resource})
  • update — Update an existing resource (PUT /fhir/R4/{Resource}/{id})

Scopes

Scopes are shared across FHIR releases — the same system/Appointment.* scope grants Appointment access on both /fhir/R4 and /fhir/R5.

Search Parameters

ParameterTypeNotes
_idtokenExact match. For identifiers use `system
patientreferenceResource reference — supply the UUID of the referenced resource.
practitionerreferenceResource reference — supply the UUID of the referenced resource.
datedateSupports FHIR date prefixes: eq, ne, gt, ge, lt, le. Format: [prefix]YYYY-MM-DD.
statustokenExact match. For identifiers use `system
appointment-typetokenExact match. For identifiers use `system
_countnumberInteger. For _count: default 20, max 100.
_cursorstringCase-insensitive partial match.

Endpoints

Read Appointment

Retrieve a single Appointment resource by its ID.

GET/fhir/R4/Appointment/{id}

Required scope: system/Appointment.rs

Response — 200

{
  "resourceType": "Appointment",
  "id": "770e8400-e29b-41d4-a716-446655440010",
  "status": "booked",
  "start": "2026-06-01T09:00:00Z",
  "end": "2026-06-01T09:30:00Z",
  "participant": [
    {
      "actor": {
        "reference": "Patient/550e8400-e29b-41d4-a716-446655440001",
        "display": "Maria Garcia"
      },
      "status": "accepted"
    },
    {
      "actor": {
        "reference": "Practitioner/880e8400-e29b-41d4-a716-446655440020",
        "display": "Dr. Rodriguez"
      },
      "status": "accepted"
    }
  ]
}

Code Samples

TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Appointment.rs" \
  | jq -r .access_token)

curl -X GET https://api.huli.ai/fhir/R4/Appointment/${RESOURCE_ID} \
  -H "Authorization: Bearer ${TOKEN}"
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Appointment/${id}`,
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Appointment/{resource_id}",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class getAppointmentExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Appointment/" + resourceId))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
import (
    "fmt"
    "net/http"
    "os"
)

func getAppointmentExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("GET", "https://api.huli.ai/fhir/R4/Appointment/"+resourceID+"", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}

Errors

CodeStatusDescription
HPB-00106401Authentication failed
HPB-00104403Insufficient scope
HPB-00102404Resource not found
HPB-00105429Rate limit exceeded

Update Appointment

Update an existing Appointment. The target status drives the operation:

  • status: cancelled → cancels the appointment. Requires the appointments.delete permission and a cancelationReason that resolves to one of the organization's active cancellation reasons (matched by the coding code or a SNOMED coding). The cancel is blocked (409) when a clinical encounter is linked to the appointment, and rejected (422) when the reason is missing or unresolvable. comment maps to the cancellation note.
  • status: entered-in-error → marks the appointment as a data-entry error (a correction, not a cancellation). Requires the appointments.edit permission; no cancellation reason is needed.
  • any other change (reschedule / field edit) → re-runs the double-booking / availability / calendar-scope checks; an overlapping slot is rejected with 409. Requires appointments.edit. A field edit carries the full appointment shape (start, end, participants, serviceType). Supports optimistic concurrency: supply the If-Match header with the ETag from the last read to make the update conditional. A stale validator is rejected with a 409 version conflict (distinct from the double-booking 409, though both carry HPB-00103). The response carries ETag + Last-Modified for the new version.
PUT/fhir/R4/Appointment/{id}

Required scope: system/Appointment.cru

Request Body

nametyperequireddescription
resourceTypestringrequired
idstring(uuid)optional
metaobjectoptional
statusstringrequiredAppointment status. On create the appointment is always booked. A PUT with `cancelled` cancels (requires a resolvable `cancelationReason`); `entered-in-error` marks a data-entry error.
serviceTypearrayoptionalThe organization service the appointment is booked for. **Required on write.** `serviceType[0].coding[0]` must use the `https://fhir.huli.ai/r4/CodeSystem/org-service` system with the Huli service UUID as the `code`.
cancelationReasonobjectoptionalReason an appointment was cancelled. Required when `status` is `cancelled`; resolved against the organization's active cancellation reasons by coding `code` or SNOMED coding.
startstring(date-time)optionalStart time (RFC 3339).
endstring(date-time)optionalEnd time (RFC 3339).
priorityintegeroptionalPriority (0 = routine).
descriptionstringoptionalDescription or reason for the appointment.
patientInstructionstringoptionalDetailed instructions for the patient.
commentstringoptionalAdditional comments. On cancel this carries the cancellation note.
participantarrayoptionalAppointment participants. The patient is a `Patient` actor; practitioners are `Practitioner` actors; the room is a `Location` actor; equipment are `Device` actors. On write, at least one `Practitioner` and one `Location` (room) participant are required.

Request Example

{
  "resourceType": "Appointment",
  "status": "cancelled",
  "cancelationReason": {
    "coding": [
      {
        "system": "http://snomed.info/sct",
        "code": "185332005"
      }
    ],
    "text": "Patient request"
  },
  "comment": "Patient called to cancel"
}

Code Samples

TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Appointment.cru" \
  | jq -r .access_token)

curl -X PUT https://api.huli.ai/fhir/R4/Appointment/${RESOURCE_ID} \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/fhir+json" \
  -d @resource.json
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Appointment/${id}`,
  {
    method: "PUT",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/fhir+json",
    },
    body: JSON.stringify(payload),
  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/fhir+json"}

resp = requests.put(
    f"https://api.huli.ai/fhir/R4/Appointment/{resource_id}",
    headers=headers,
    json=payload,
)
resp.raise_for_status()
print(resp.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class updateAppointmentExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String resourceId = "RESOURCE_ID";
        String payload = "{}"; // your serialized FHIR resource

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Appointment/" + resourceId))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .header("Content-Type", "application/fhir+json")
        .method("PUT", HttpRequest.BodyPublishers.ofString(payload))
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
import (
    "fmt"
    "net/http"
    "os"
)

func updateAppointmentExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("PUT", "https://api.huli.ai/fhir/R4/Appointment/"+resourceID+"", body)
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/fhir+json")
    // set req.Body to your serialized resource
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}

Errors

CodeStatusDescription
HPB-00101400Validation error
HPB-00106401Authentication failed
HPB-00104403Insufficient scope
HPB-00102404Resource not found
HPB-00103409Version conflict
HPB-00101422Unprocessable entity
HPB-00105429Rate limit exceeded

Search Appointments

Search for Appointment resources using FHIR search parameters.

GET/fhir/R4/Appointment

Required scope: system/Appointment.rs

Query Parameters

nametyperequireddescription
patientstringoptionalPatient reference (UUID).
practitionerstringoptionalPractitioner reference (UUID).
datestringoptionalAppointment date. Supports FHIR date prefixes: `eq`, `ne`, `gt`, `ge`, `lt`, `le`. Recommended for paginated reads: the appointment store is range-partitioned by start time, so supplying `date` lets the query prune to the relevant partitions. A date-less search must merge across every partition, which gets more expensive as the rolling partition window grows.
statusstringoptionalAppointment status: `proposed`, `booked`, `arrived`, `fulfilled`, `cancelled`, `noshow`.
_countintegeroptionalNumber of results per page (default: 20, max: 100).
_cursorstringoptionalOpaque pagination cursor from the `next` link of a previous search result.

Code Samples

TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Appointment.rs" \
  | jq -r .access_token)

curl -X GET https://api.huli.ai/fhir/R4/Appointment \
  -H "Authorization: Bearer ${TOKEN}"
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Appointment`,
  {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${token}`,
    },

  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}"}

resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Appointment",
    headers=headers,
)
resp.raise_for_status()
print(resp.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class searchAppointmentsExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Appointment"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .method("GET", HttpRequest.BodyPublishers.noBody())
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
import (
    "fmt"
    "net/http"
    "os"
)

func searchAppointmentsExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("GET", "https://api.huli.ai/fhir/R4/Appointment", nil)
    req.Header.Set("Authorization", "Bearer "+token)

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}

Errors

CodeStatusDescription
HPB-00106401Authentication failed
HPB-00104403Insufficient scope
HPB-00105429Rate limit exceeded

Create Appointment

Create a new Appointment resource. The server assigns the resource ID and always books the appointment in the booked state. The create runs the full scheduling rule set, so the request body must name the resources an appointment requires:

  • serviceType (required) — serviceType[0].coding[0] must reference a Huli organization service via the https://fhir.huli.ai/r4/CodeSystem/org-service code system, with the service UUID as the code. The service drives appointment type, specialty, booking policy, and the per-service resource requirements. A missing or unresolvable serviceType is rejected (400) — it is a structural-validation failure (all of which return 400; 422 is reserved for business-rule rejections).
  • At least one Practitioner participant and one Location (room) participant — appointments are unschedulable without them. Equipment is supplied as Device participants. The server runs double-booking / availability / calendar-scope checks: a time slot that overlaps an existing booking for any participant is rejected with 409. Telehealth services auto-provision a telemedicine session. Requires the appointments.create permission in addition to the system/Appointment.cru scope.
POST/fhir/R4/Appointment

Required scope: system/Appointment.cru

Request Body

nametyperequireddescription
resourceTypestringrequired
idstring(uuid)optional
metaobjectoptional
statusstringrequiredAppointment status. On create the appointment is always booked. A PUT with `cancelled` cancels (requires a resolvable `cancelationReason`); `entered-in-error` marks a data-entry error.
serviceTypearrayoptionalThe organization service the appointment is booked for. **Required on write.** `serviceType[0].coding[0]` must use the `https://fhir.huli.ai/r4/CodeSystem/org-service` system with the Huli service UUID as the `code`.
cancelationReasonobjectoptionalReason an appointment was cancelled. Required when `status` is `cancelled`; resolved against the organization's active cancellation reasons by coding `code` or SNOMED coding.
startstring(date-time)optionalStart time (RFC 3339).
endstring(date-time)optionalEnd time (RFC 3339).
priorityintegeroptionalPriority (0 = routine).
descriptionstringoptionalDescription or reason for the appointment.
patientInstructionstringoptionalDetailed instructions for the patient.
commentstringoptionalAdditional comments. On cancel this carries the cancellation note.
participantarrayoptionalAppointment participants. The patient is a `Patient` actor; practitioners are `Practitioner` actors; the room is a `Location` actor; equipment are `Device` actors. On write, at least one `Practitioner` and one `Location` (room) participant are required.

Request Example

{
  "resourceType": "Appointment",
  "status": "booked",
  "start": "2026-06-15T14:00:00Z",
  "end": "2026-06-15T14:30:00Z",
  "serviceType": [
    {
      "coding": [
        {
          "system": "https://fhir.huli.ai/r4/CodeSystem/org-service",
          "code": "990e8400-e29b-41d4-a716-446655440030"
        }
      ]
    }
  ],
  "participant": [
    {
      "actor": {
        "reference": "Patient/550e8400-e29b-41d4-a716-446655440001"
      },
      "status": "accepted"
    },
    {
      "actor": {
        "reference": "Practitioner/880e8400-e29b-41d4-a716-446655440020"
      },
      "status": "accepted"
    },
    {
      "actor": {
        "reference": "Location/aa0e8400-e29b-41d4-a716-446655440040"
      },
      "status": "accepted"
    }
  ]
}

Code Samples

TOKEN=$(curl -s -X POST https://api.huli.ai/auth/token \
  -d "grant_type=client_credentials" \
  -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
  -d "client_assertion=${CLIENT_ASSERTION}" \
  -d "scope=system/Appointment.cru" \
  | jq -r .access_token)

curl -X POST https://api.huli.ai/fhir/R4/Appointment \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/fhir+json" \
  -d @appointment.json
const token = process.env.HULI_ACCESS_TOKEN ?? "";

const response = await fetch(
  `https://api.huli.ai/fhir/R4/Appointment`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/fhir+json",
    },
    body: JSON.stringify(payload),
  }
);

if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
const data: unknown = await response.json();
import os
import requests

token = os.environ["HULI_ACCESS_TOKEN"]
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/fhir+json"}

resp = requests.post(
    f"https://api.huli.ai/fhir/R4/Appointment",
    headers=headers,
    json=payload,
)
resp.raise_for_status()
print(resp.json())
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class createAppointmentExample {
    public static void main(String[] args) throws Exception {
        String token = System.getenv("HULI_ACCESS_TOKEN");
        String payload = "{}"; // your serialized FHIR resource

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.huli.ai/fhir/R4/Appointment"))
        .header("Authorization", "Bearer " + token)
        .header("Accept", "application/fhir+json")
        .header("Content-Type", "application/fhir+json")
        .method("POST", HttpRequest.BodyPublishers.ofString(payload))
        .build();

        HttpResponse<String> response =
        client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("status: " + response.statusCode());
        System.out.println(response.body());
    }
}
import (
    "fmt"
    "net/http"
    "os"
)

func createAppointmentExample() {
    token := os.Getenv("HULI_ACCESS_TOKEN")
    req, _ := http.NewRequest("POST", "https://api.huli.ai/fhir/R4/Appointment", body)
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/fhir+json")
    // set req.Body to your serialized resource
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    fmt.Println("status:", resp.Status)
}

Errors

CodeStatusDescription
HPB-00101400Validation error
HPB-00106401Authentication failed
HPB-00104403Insufficient scope
HPB-00103409Version conflict
HPB-00101422Unprocessable entity
HPB-00105429Rate limit exceeded