Content negotiation & errors
Three HTTP mechanisms cut across every openEHR resource: choosing the wire
format (JSON or XML), controlling how much a write returns (the Prefer
header), and versioned optimistic concurrency (ETag and If-Match). This
chapter explains all three and the shape of error responses, so the examples in
Resource walkthroughs make sense in general.
JSON and XML
EHRbase-rs speaks canonical JSON and canonical XML for the RM-typed resources. Choose with the standard HTTP headers:
- Request body: set
Content-Type: application/jsonorapplication/xml. - Response: set
Accept: application/jsonorapplication/xml.
JSON is wired end to end for every operation. XML is supported for the
spec-typed RM objects — a single composition, EHR_STATUS, EHR, FOLDER, and
the version family (versioned objects and revision history) — whose canonical
XML shape the openEHR ITS-XML schemas define. Responses that are not a
spec-typed RM value (collections, item tags, and the query and terminology
DTOs) are JSON-only, as is the CONTRIBUTION envelope.
# Commit a composition as XML, ask for XML back
curl -u ehrbase:ehrbase \
-H 'Content-Type: application/xml' \
-H 'Accept: application/xml' \
--data-binary @composition.xml \
http://localhost:8080/ehrbase/rest/openehr/v1/ehr/$EHR_ID/composition
For compositions and templates, several endpoints additionally accept the Better
WebTemplate, FLAT (simSDT), and STRUCTURED (structSDT) JSON media
types — application/openehr.wt+json, application/openehr.wt.flat+json, and
application/openehr.wt.structured+json. These are covered in
Templates & validation.
The query API is JSON only — it does not accept XML or the WebTemplate media types.
The Prefer header
Write operations (create/update) accept a Prefer header controlling the
response body. Its default is return=minimal:
Prefer value | Effect |
|---|---|
return=minimal (default) | Empty body; the identifier is in ETag/Location. Status 204 on update, 201 on create. |
return=representation | The full created/updated resource in the body, status 200/201. |
return=identifier | Just the resource identifier object. |
Use return=representation when you want the server-completed object back
(with its assigned version id and any server-set audit fields); use
return=minimal for throughput when you only need the id.
ETag and If-Match — optimistic concurrency
openEHR objects are versioned, and updates use HTTP preconditions to prevent lost updates:
-
Every read and successful write returns an
ETagheader carrying the object or version identifier (a weak ETag,W/"..."). -
Updating or deleting a versioned object requires an
If-Matchheader set to the current version id, in double quotes:If-Match: "8849182c-82ad-4088-a07f-48ead4180515::your.system::2" -
If the object has moved on since you read it, the write fails with 412 Precondition Failed and the current version id in the response
ETag. Re-read, reconcile, and retry against the new version.
Location may appear on responses too, but treat it as informational for reads
(it is marked deprecated on retrieval responses in the contract); the ETag is
the authoritative identifier.
Tip
The round-trip is: read the resource → keep its
ETagvalue → send it back asIf-Matchon the update → get a newETagfor the version you just created. Never fabricate a version id; always echo the one the server gave you.
Error responses
Errors use conventional HTTP status codes (see the summary in Resource walkthroughs) with one of two JSON body shapes:
-
Validation errors (a composition that fails its template) use the openEHR error shape:
{ "message": "Composition validation failed", "validationErrors": [ "/content[0]/data/events[0]/data/items[1]/value/magnitude: value out of range", "/content[0]/data/events[0]/data/items[2]/value/defining_code: code not in group" ] }Each entry is
"<path>: <message>", so a client can point the user at the exact offending node. -
All other errors use a simple shape — the status reason plus a message:
{ "error": "Not Found", "message": "No EHR with id ..." }
Match on the HTTP status first; read the body for the human-readable detail and, for validation, the per-node list.