# 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.

Source: https://developers.anpheros.com/guides/store-medical-records-fhir

**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](https://developers.anpheros.com/guides/fhir) 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](https://developers.anpheros.com/guides/fhir) are listed in the capability statement at `/fhir/R4/metadata`.

## Before you start

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

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

## Step 1: create a Patient

```bash
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:

```json
{"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.

```bash
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.

```bash
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:

- **Codes, not labels.** `85354-9` means "blood pressure panel" in every FHIR system; a free-text "BP" does not. Look codes up with `GET /v1/terminology/loinc?q=blood pressure` ([FHIR code systems](https://developers.anpheros.com/guides/fhir-code-systems)).
- **UCUM units** (`mm[Hg]`, `kg`, `mg/dL`) in `valueQuantity.code`, so values can be compared and converted.
- **`Idempotency-Key`.** A retry after a timeout returns the first response instead of storing the reading twice.

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](https://developers.anpheros.com/guides/import-lab-results-fhir).

## Step 3: record a diagnosis as a Condition

```bash
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:

```bash
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

```bash
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:

```bash
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:

```bash
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:

```bash
curl "$BASE/Provenance?target=Observation/$OBS" -H "Authorization: Bearer $KEY"
```

```json
{"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

```bash
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](https://developers.anpheros.com/guides/fhir)).

## 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](https://developers.anpheros.com/guides/medical-data-api) and the [SDKs](https://developers.anpheros.com/guides/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](https://developers.anpheros.com/guides/fhir-server-comparison) weighs the options, and [FHIR vs. a custom medical database](https://developers.anpheros.com/guides/fhir-vs-custom-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

- [FHIR platform](https://developers.anpheros.com/guides/fhir): the concepts, resource types and operations
- [FHIR code systems](https://developers.anpheros.com/guides/fhir-code-systems): LOINC, ICD-10, ATC, UCUM
- [How to import laboratory results into a FHIR record](https://developers.anpheros.com/guides/import-lab-results-fhir)
- [Medical data API](https://developers.anpheros.com/guides/medical-data-api): the simpler REST view of the same data
- [Free FHIR database and sandbox](https://developers.anpheros.com/guides/free-fhir-database)
