Cadasto demographic link — connecting an EHR to a demographic person
Cadasto exposes a vendor-extension header that lets clients link an EHR to a Demographic PERSON in the same call that creates or updates the EHR. The link is stored separately from the openEHR EHR_STATUS.subject field — both can be set independently — and is used internally by the platform to resolve a person to their EHR (and back) without an AQL detour.
Two headers govern the feature:
| Header | Direction | Where | Purpose |
|---|---|---|---|
cadasto-person-uid |
request | POST /ehr, PUT /ehr/{ehr_id}, PUT /ehr/{ehr_id}/ehr_status |
Write the link between the EHR and the supplied PERSON. |
include-cadasto-person-uid |
request | GET /ehr/{ehr_id} |
Opt in to receiving the linked PERSON UID back in the response. |
cadasto-person-uid |
response | GET /ehr/{ehr_id} |
The linked PERSON UID, when the opt-in header was set and a link exists. |
After reading this guide, you should be able to:
- Link an EHR to a Cadasto PERSON in one call when creating the EHR
- Backfill the link on an existing unlinked EHR via the EHR_STATUS update endpoint
- Retrieve the linked PERSON UID from a
GET /ehr/{ehr_id}call - Understand the append-only semantics and conflict responses
Cadasto extension. The cadasto-person-uid and include-cadasto-person-uid headers described on this page are not part of the openEHR ITS-REST specification. Standard openEHR clients can continue to use the EHR_STATUS.subject.external_ref pattern together with GET /ehr?subject_id=…&subject_namespace=… to identify EHR subjects — the two mechanisms are independent and can coexist on the same EHR.
Two ways to establish the link
Both routes write to the same underlying link store and share the same conflict rules.
| Entry point | Use when |
|---|---|
cadasto-person-uid on POST /ehr or PUT /ehr/{ehr_id} |
A new EHR is being created for a PERSON that already exists in the Demographic API. |
cadasto-person-uid on PUT /ehr/{ehr_id}/ehr_status |
An existing EHR has no link yet and you want to backfill one. |
On EHR_STATUS update the operation is append-only: once an EHR has a link, the header cannot be used to re-link it to a different PERSON. See Cadasto-specific behaviour below.
The cadasto-person-uid request header
Request format
A single bare PERSON UID, in the HIER_OBJECT_ID form — the same form returned as uid.value by the Demographic API on create:
cadasto-person-uid: 7d44b88c-4199-4bad-97dc-d78268e01398
The versioned form (<uuid>::<system>::<version>) is not accepted. Strip any version suffix before sending — for example, with PHP Engine24_Helper::extractUid($personUid) or any standard UUID regex.
Endpoints that accept it
| Endpoint | Effect when the header is set |
|---|---|
POST /ehr |
Creates the EHR and writes the link in the same contribution. |
PUT /ehr/{ehr_id} (create-with-id) |
Same as above, with a client-supplied ehr_id. |
PUT /ehr/{ehr_id}/ehr_status |
Writes the link in the same contribution as the EHR_STATUS version, only if no link exists yet. |
The header is ignored on all other endpoints. Omitting it preserves the existing behaviour — none of these endpoints write a link without it.
Example: create an EHR linked to a person in one call
POST /ehr
Authorization: Bearer <token>
Accept: application/json
Prefer: return=representation
cadasto-person-uid: 7d44b88c-4199-4bad-97dc-d78268e01398
HTTP/1.1 201 Created
ETag: W/"3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718"
Location: /ehr/3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718
Content-Type: application/json
{
"system_id": { "value": "cadasto.io" },
"ehr_id": { "value": "3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718" },
"ehr_status": { "...": "..." },
"time_created": { "value": "2026-06-08T10:30:00.000+00:00" }
}
The EHR is now linked to PERSON 7d44b88c-…. The link does not appear in the response body — see Reading the link back for how to retrieve it on subsequent calls.
Example: backfill a link on an existing EHR
PUT /ehr/3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718/ehr_status
Authorization: Bearer <token>
Content-Type: application/json
If-Match: "3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718::cadasto.io::1"
Prefer: return=minimal
cadasto-person-uid: 7d44b88c-4199-4bad-97dc-d78268e01398
{ "_type": "EHR_STATUS", "...": "..." }
HTTP/1.1 204 No Content
ETag: W/"3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718::cadasto.io::2"
Location: /ehr/3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718/ehr_status/3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718::cadasto.io::2
The EHR_STATUS version is incremented and the link is written in the same atomic contribution.
Reading the link back
The include-cadasto-person-uid opt-in
GET /ehr/{ehr_id} does not emit the link by default. Add the request header include-cadasto-person-uid to opt in:
GET /ehr/3f9b8aa1-7c63-4d12-9e05-2a4d8b0a2718
Authorization: Bearer <token>
Accept: application/json
include-cadasto-person-uid: 1
The header is presence-triggered — the value is ignored, consistent with how Prefer: include_item_tags works for ITEM_TAG. Use any non-empty string (1, true, the literal text yes).
Response
When the EHR has a link, the response carries it as a cadasto-person-uid header:
HTTP/1.1 200 OK
Content-Type: application/json
cadasto-person-uid: 7d44b88c-4199-4bad-97dc-d78268e01398
{ "_type": "EHR", "ehr_id": { "value": "3f9b8aa1-..." }, "...": "..." }
When the EHR has no link, the response header is omitted entirely (not sent with an empty value). When the request did not opt in via include-cadasto-person-uid, the extra link lookup is skipped and the header is also omitted.
Cadasto-specific behaviour
[!warning] The following points constrain how the link behaves on the Cadasto platform. Sending an unsupported value or attempting a forbidden re-link returns
400 Bad Request,409 Conflict, or422 Unprocessable Entitydepending on the case — see Validation and error responses.
Append-only on EHR_STATUS update
Once an EHR has a link, the cadasto-person-uid header on PUT /ehr/{ehr_id}/ehr_status will not re-link it. The request is rejected with 409 Conflict. To re-link an EHR you must first delete it via DELETE /ehr/{ehr_id} (which also removes the link), then create a new EHR.
Independent of subject.external_ref
The link and the openEHR EHR_STATUS.subject.external_ref.id/namespace fields are independent. You can set:
- Neither — the EHR has no Cadasto person link and no external subject reference.
- Subject only — standard openEHR behaviour;
GET /ehr?subject_id=…&subject_namespace=…resolves via the EHR_STATUS XML. - Cadasto link only — the EHR has no
subject.external_refbut is resolvable via the demographic store. - Both — independently set, neither overrides the other.
The duplicate-subject check on POST /ehr (which rejects a body whose subject.external_ref matches an existing EHR's subject with 409) is unaffected by this feature and continues to operate on the body content.
PERSON-typed parties only
The header value must resolve to a PERSON in the Demographic API. Organisations, Agents, Groups, and Roles are not accepted and return 422 Unprocessable Entity.
Not part of the openEHR ITS-REST specification
This is a Cadasto-specific extension. Clients that need to remain on the standard spec surface can continue to use EHR_STATUS.subject.external_ref and the standard GET /ehr?subject_id=…&subject_namespace=… lookup.
Validation and error responses
| Endpoint | Condition | Status |
|---|---|---|
| Any write endpoint | Header value is not a well-formed bare UUID (e.g. versioned form sent) | 400 Bad Request |
| Any write endpoint | The PERSON does not exist, or the UID resolves to a non-PERSON actor | 422 Unprocessable Entity |
| Any write endpoint | The supplied PERSON is already linked to another EHR | 409 Conflict |
PUT /ehr/{ehr_id}/ehr_status only |
The target EHR already has a link to any PERSON | 409 Conflict (append-only) |
PUT /ehr/{ehr_id}/ehr_status |
The target EHR does not exist | 404 Not Found |
On EHR_STATUS update the EHR-exists check runs before the cadasto pre-checks, so a 404 for a non-existent EHR takes precedence over the cadasto 409s — this prevents the response from leaking link state for an unknown EHR.
Sample 409 Conflict body
{
"title": "Conflict",
"errors": [
{ "detail": "The supplied PERSON is already linked to another EHR." }
]
}
{
"title": "Conflict",
"errors": [
{ "detail": "The EHR is already linked to a PERSON. The cadasto-person-uid header is append-only." }
]
}
API references
Next steps
- Quick Start — Walk through EHR creation, with and without the link
- Demographic API — Manage PERSONs to link against
- openEHR concepts — Demographics — Background on the PARTY hierarchy and why demographic data is separated from clinical content