Appointment (FHIR R4)
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.
- system/Appointment.rs — see scope reference
- system/Appointment.cru — see scope reference
Search Parameters
| Parameter | Type | Notes |
|---|---|---|
_id | token | Exact match. For identifiers use `system |
patient | reference | Resource reference — supply the UUID of the referenced resource. |
practitioner | reference | Resource reference — supply the UUID of the referenced resource. |
date | date | Supports FHIR date prefixes: eq, ne, gt, ge, lt, le. Format: [prefix]YYYY-MM-DD. |
status | token | Exact match. For identifiers use `system |
appointment-type | token | Exact match. For identifiers use `system |
_count | number | Integer. For _count: default 20, max 100. |
_cursor | string | Case-insensitive partial match. |
Endpoints
Read Appointment
Retrieve a single Appointment resource by its ID.
/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
| Code | Status | Description |
|---|---|---|
| HPB-00106 | 401 | Authentication failed |
| HPB-00104 | 403 | Insufficient scope |
| HPB-00102 | 404 | Resource not found |
| HPB-00105 | 429 | Rate limit exceeded |
Update Appointment
Update an existing Appointment. The target status drives the operation:
status: cancelled→ cancels the appointment. Requires theappointments.deletepermission and acancelationReasonthat resolves to one of the organization's active cancellation reasons (matched by the codingcodeor 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.commentmaps to the cancellation note.status: entered-in-error→ marks the appointment as a data-entry error (a correction, not a cancellation). Requires theappointments.editpermission; 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. Requiresappointments.edit. A field edit carries the full appointment shape (start,end, participants,serviceType). Supports optimistic concurrency: supply theIf-Matchheader with theETagfrom the last read to make the update conditional. A stale validator is rejected with a409version conflict (distinct from the double-booking409, though both carry HPB-00103). The response carriesETag+Last-Modifiedfor the new version.
/fhir/R4/Appointment/{id}Required scope: system/Appointment.cru
Request Body
| name | type | required | description |
|---|---|---|---|
| resourceType | string | required | |
| id | string(uuid) | optional | |
| meta | object | optional | |
| status | string | required | Appointment 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. |
| serviceType | array | optional | The 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`. |
| cancelationReason | object | optional | Reason an appointment was cancelled. Required when `status` is `cancelled`; resolved against the organization's active cancellation reasons by coding `code` or SNOMED coding. |
| start | string(date-time) | optional | Start time (RFC 3339). |
| end | string(date-time) | optional | End time (RFC 3339). |
| priority | integer | optional | Priority (0 = routine). |
| description | string | optional | Description or reason for the appointment. |
| patientInstruction | string | optional | Detailed instructions for the patient. |
| comment | string | optional | Additional comments. On cancel this carries the cancellation note. |
| participant | array | optional | Appointment 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
| Code | Status | Description |
|---|---|---|
| HPB-00101 | 400 | Validation error |
| HPB-00106 | 401 | Authentication failed |
| HPB-00104 | 403 | Insufficient scope |
| HPB-00102 | 404 | Resource not found |
| HPB-00103 | 409 | Version conflict |
| HPB-00101 | 422 | Unprocessable entity |
| HPB-00105 | 429 | Rate limit exceeded |
Search Appointments
Search for Appointment resources using FHIR search parameters.
/fhir/R4/AppointmentRequired scope: system/Appointment.rs
Query Parameters
| name | type | required | description |
|---|---|---|---|
| patient | string | optional | Patient reference (UUID). |
| practitioner | string | optional | Practitioner reference (UUID). |
| date | string | optional | Appointment 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. |
| status | string | optional | Appointment status: `proposed`, `booked`, `arrived`, `fulfilled`, `cancelled`, `noshow`. |
| _count | integer | optional | Number of results per page (default: 20, max: 100). |
| _cursor | string | optional | Opaque 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
| Code | Status | Description |
|---|---|---|
| HPB-00106 | 401 | Authentication failed |
| HPB-00104 | 403 | Insufficient scope |
| HPB-00105 | 429 | Rate 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 thehttps://fhir.huli.ai/r4/CodeSystem/org-servicecode system, with the service UUID as thecode. The service drives appointment type, specialty, booking policy, and the per-service resource requirements. A missing or unresolvableserviceTypeis rejected (400) — it is a structural-validation failure (all of which return400;422is reserved for business-rule rejections).- At least one Practitioner participant and one Location (room) participant — appointments are unschedulable without them. Equipment is supplied as
Deviceparticipants. The server runs double-booking / availability / calendar-scope checks: a time slot that overlaps an existing booking for any participant is rejected with409. Telehealth services auto-provision a telemedicine session. Requires theappointments.createpermission in addition to thesystem/Appointment.cruscope.
/fhir/R4/AppointmentRequired scope: system/Appointment.cru
Request Body
| name | type | required | description |
|---|---|---|---|
| resourceType | string | required | |
| id | string(uuid) | optional | |
| meta | object | optional | |
| status | string | required | Appointment 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. |
| serviceType | array | optional | The 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`. |
| cancelationReason | object | optional | Reason an appointment was cancelled. Required when `status` is `cancelled`; resolved against the organization's active cancellation reasons by coding `code` or SNOMED coding. |
| start | string(date-time) | optional | Start time (RFC 3339). |
| end | string(date-time) | optional | End time (RFC 3339). |
| priority | integer | optional | Priority (0 = routine). |
| description | string | optional | Description or reason for the appointment. |
| patientInstruction | string | optional | Detailed instructions for the patient. |
| comment | string | optional | Additional comments. On cancel this carries the cancellation note. |
| participant | array | optional | Appointment 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
| Code | Status | Description |
|---|---|---|
| HPB-00101 | 400 | Validation error |
| HPB-00106 | 401 | Authentication failed |
| HPB-00104 | 403 | Insufficient scope |
| HPB-00103 | 409 | Version conflict |
| HPB-00101 | 422 | Unprocessable entity |
| HPB-00105 | 429 | Rate limit exceeded |