AnpherosAnpheros PlatformDevelopers

How to store medical records using FHIR R4

Tutorial: create a Patient, a blood-pressure Observation and a Condition, use a transaction, search, update with history and read provenance — every request with curl and the real response, on the free sandbox.

This tutorial stores a small medical record as HL7 FHIR R4 resources: a Patient, a blood-pressure Observation and a diagnosis as a Condition, then a patient and a measurement together in one transaction. It searches them, updates the diagnosis without losing the earlier version, and reads who wrote what. Every request is plain curl against the free Anpheros sandbox, and every response shown is what the API returns.

If you are new to FHIR, read FHIR platform first for the concepts. This page is the hands-on part.

The three resources most records start with

Resource What it holds Coded with
Patient who the record is about: name, birth date, gender, identifiers from other systems your identifier systems
Observation anything measured or reported: vital signs, lab results, a weight, a symptom score LOINC, UCUM units
Condition a diagnosis or a health problem, with its status over time ICD-10 (SNOMED CT behind a licence flag)

Medications (MedicationStatement), allergies (AllergyIntolerance), immunizations and documents follow the same pattern; the 26 supported resource types are listed in the capability statement at /fhir/R4/metadata.

Before you start

Get a sandbox key (sk_test_…) at platform.anpheros.com/dashboard with Get a sandbox key. Nothing you create there is real patient data, and nothing is visible to other projects.

export BASE=https://platform.anpheros.com/fhir/R4
export KEY=sk_test_…

Step 1: create a Patient

curl -X POST "$BASE/Patient" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" -d '{
  "resourceType": "Patient",
  "identifier": [{"system": "https://clinic.example/mrn", "value": "MRN-10442"}],
  "name": [{"family": "Marin", "given": ["Ioana"]}],
  "gender": "female",
  "birthDate": "1979-04-12"
}'

201 Created, with the id and version the server assigned:

{"resourceType": "Patient",
 "identifier": [{"system": "https://clinic.example/mrn", "value": "MRN-10442"}],
 "name": [{"family": "Marin", "given": ["Ioana"]}], "gender": "female", "birthDate": "1979-04-12",
 "id": "d09e3942-ed86-4458-b099-fa674ab21b84",
 "meta": {"versionId": "1", "lastUpdated": "2026-10-12T20:16:51.043Z"}}

Keep your own ids in identifier with a system you control (here a clinic's record number), so you can always find the patient again with GET $BASE/Patient?identifier=https://clinic.example/mrn|MRN-10442. The FHIR id is the platform's, and it is specific to your project: another project sees the same person under a different id.

export PID=d09e3942-ed86-4458-b099-fa674ab21b84

Step 2: store a measurement as an Observation

Blood pressure is one observation with two components, systolic and diastolic, each with its own LOINC code. Say who measured it: here a home device.

curl -X POST "$BASE/Observation" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
     -H "X-Anpheros-Author-Type: device" -H "Idempotency-Key: bp-2026-10-10-0830" -d '{
  "resourceType": "Observation",
  "status": "final",
  "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "vital-signs"}]}],
  "code": {"coding": [{"system": "http://loinc.org", "code": "85354-9", "display": "Blood pressure panel"}]},
  "subject": {"reference": "Patient/'$PID'"},
  "effectiveDateTime": "2026-10-10T08:30:00Z",
  "component": [
    {"code": {"coding": [{"system": "http://loinc.org", "code": "8480-6"}]},
     "valueQuantity": {"value": 138, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}},
    {"code": {"coding": [{"system": "http://loinc.org", "code": "8462-4"}]},
     "valueQuantity": {"value": 88, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"}}
  ]
}'

The response is the stored resource with id and meta.versionId: "1". Three details matter for a usable record:

Lab results are the same resource with category laboratory and usually a referenceRange. To bring in whole lab reports, see How to import laboratory results into a FHIR record.

Step 3: record a diagnosis as a Condition

curl -X POST "$BASE/Condition" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
     -H "X-Anpheros-Author-Type: practitioner" -d '{
  "resourceType": "Condition",
  "subject": {"reference": "Patient/'$PID'"},
  "clinicalStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active"}]},
  "verificationStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", "code": "confirmed"}]},
  "code": {"coding": [{"system": "http://hl7.org/fhir/sid/icd-10", "code": "I10", "display": "Essential (primary) hypertension"}]},
  "onsetDateTime": "2026-03-02"
}'

clinicalStatus (active, resolved, in remission) and verificationStatus (confirmed, provisional) are separate on purpose: a suspected diagnosis is not the same as a confirmed one, and a resolved problem is still part of the history.

Step 4: create related resources in one transaction

When a patient and their first measurement arrive together, for example from a clinic system, send them as a transaction Bundle. Either everything is stored or nothing is. References between new resources use temporary urn:uuid: ids that the server replaces:

curl -X POST "$BASE" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" -d '{
  "resourceType": "Bundle", "type": "transaction",
  "entry": [
    {"fullUrl": "urn:uuid:9f1c0d1e-0000-4000-8000-000000000001",
     "resource": {"resourceType": "Patient", "name": [{"family": "Stan", "given": ["Mihai"]}], "gender": "male", "birthDate": "1990-02-01"},
     "request": {"method": "POST", "url": "Patient"}},
    {"resource": {"resourceType": "Observation", "status": "final",
       "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "vital-signs"}]}],
       "code": {"coding": [{"system": "http://loinc.org", "code": "29463-7", "display": "Body weight"}]},
       "subject": {"reference": "urn:uuid:9f1c0d1e-0000-4000-8000-000000000001"},
       "effectiveDateTime": "2026-10-10",
       "valueQuantity": {"value": 82.4, "unit": "kg", "system": "http://unitsofmeasure.org", "code": "kg"}},
     "request": {"method": "POST", "url": "Observation"}}
  ]
}'

The transaction-response has one entry per resource, with "status": "201 Created", its location and the stored resource. The Observation's subject now points to the real Patient/fad3665a-….

Step 5: search

curl "$BASE/Observation?patient=$PID&code=http://loinc.org|85354-9&date=ge2026-10-01&_sort=-date" \
     -H "Authorization: Bearer $KEY"

You get a searchset Bundle with total and the matching resources, newest first. Other searches you will use often: Condition?patient=$PID&clinical-status=active, Observation?patient=$PID&category=laboratory, and Patient/$PID/$everything for the whole record.

Step 6: update without losing history

The hypertension resolves. Change the status and add the end date, and use If-Match with the version you read so you never overwrite someone else's change:

curl -X PUT "$BASE/Condition/$CID" -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
     -H 'If-Match: W/"1"' -d '{ …the same Condition…, "clinicalStatus": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "resolved"}]}, "abatementDateTime": "2026-10-11" }'

200 OK with ETag: W/"2". If another write got there first, you get 412 and read again. The earlier version is still there:

curl "$BASE/Condition/$CID/_history" -H "Authorization: Bearer $KEY"

returns both versions: versionId 2 (resolved) and 1 (active). GET $BASE/Condition/$CID/_history/1 reads the old one. Nothing in a record is silently overwritten.

Step 7: see who wrote what

Every write creates a Provenance resource. For the blood-pressure reading:

curl "$BASE/Provenance?target=Observation/$OBS" -H "Authorization: Bearer $KEY"
{"resourceType": "Provenance",
 "target": [{"reference": "Observation/d83d9075-…/_history/1"}],
 "recorded": "2026-10-12T20:16:51.056000Z",
 "activity": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/v3-DataOperation", "code": "CREATE"}]},
 "agent": [{"type": {"coding": [{"system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", "code": "author"}]},
            "who": {"identifier": {"system": "https://anpheros.com/fhir/CodeSystem/author-type", "value": "device"}, "display": "proj_…"},
            "onBehalfOf": {"identifier": {"system": "https://anpheros.com/fhir/sid/organization", "value": "org_…"}, "display": "…"}}]}

The author type you sent (device) is recorded with the project and the organisation. Send X-Anpheros-Author-Ref: Practitioner/<id> to name the clinician behind a write.

Step 8: the record as a summary

curl "$BASE/Patient/$PID/\$summary" -H "Authorization: Bearer $KEY"

returns an International Patient Summary: a document Bundle that starts with a Composition and has sections such as Problems, Medications, Allergies and Vital signs, built from the resources above. That is the format to hand to another system or print for a doctor (FHIR platform).

Prefer JSON without FHIR?

The same data is available through a simpler REST view, /v1, with the same ids: POST /v1/patients/{id}/observations takes {"code": "85354-9", "category": "vital-signs", "effective_at": "…", "components": […]} and stores the same FHIR Observation. Use whichever fits your code. See Medical data API and the SDKs.

Frequently asked questions

What is the best way to store medical records?

As coded, versioned resources with a record of who wrote each one. HL7 FHIR R4 gives you the structure (Patient, Observation, Condition and others), standard code systems such as LOINC and ICD-10, and history; a FHIR backend adds search, consent and provenance on top.

Do I need a FHIR server to use FHIR?

You need somewhere that stores and searches FHIR resources and enforces who can read them. You can run an open-source FHIR server yourself or use a managed one such as Anpheros; How Anpheros compares with other FHIR servers weighs the options, and FHIR vs. a custom medical database covers building your own schema instead.

How do I store blood pressure in FHIR?

As one Observation with code LOINC 85354-9 and two component entries: systolic 8480-6 and diastolic 8462-4, each with a valueQuantity in mm[Hg].

Can I change a stored resource?

Yes, with PUT and If-Match. Each change creates a new version; earlier versions stay readable through _history, and Provenance records who made each change. Deletions are soft and keep the history.

Related