Clinia
Tutorials

Ingest and Query a Patient

Full walkthrough: ingest a FHIR bundle and a CDA document, browse the virtual file system, and read a condition story end-to-end.

Ingest and Query a Patient

In this tutorial you will:

  1. Ingest a FHIR R4 bundle and a CDA document for the same patient
  2. Verify the pipeline finished and entities were resolved
  3. Browse the virtual file system to discover what was extracted
  4. Read a condition story

By the end you will have a working Patient Memory workflow and understand how source records become a navigable clinical graph.

Prerequisites: a Patient Memory workspace. You will need your workspace ID and API key. Replace <workspace-id> and <clinia-api-key> in the commands below.

Time: ~15 minutes.


Step 1: Ingest a FHIR R4 bundle

Pick a registry key for the patient. This is the identifier you control. It does not need to match Patient.id inside the bundle.

curl -X POST "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/ingest/fhir" \
  -H "X-Clinia-API-Key: <clinia-api-key>" \
  -H "Content-Type: application/json" \
  -d @fhir-bundle.json

A successful response confirms how many entities and events were extracted:

{
  "ok": true,
  "source": "fhir",
  "stats": { "itemsScanned": 42, "entitiesExtracted": 12, "eventsExtracted": 8 },
  "warnings": 0,
  "patient": { "id": "123", "name": "Jane Smith" }
}

If warnings is greater than zero, some resources were skipped, usually because of missing required codes or unsupported resource types. They do not fail the ingest.

Step 2: Ingest a CDA document

Multiple ingest calls for the same registry key accumulate sources. The pipeline re-runs entity resolution after each one, merging what it can across all sources.

curl -X POST "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/ingest/cda" \
  -H "X-Clinia-API-Key: <clinia-api-key>" \
  -H "Content-Type: text/plain" \
  --data-binary @summary.xml
{
  "ok": true,
  "source": "cda",
  "stats": { "itemsScanned": 28, "entitiesExtracted": 9, "eventsExtracted": 4 },
  "warnings": 1,
  "patient": { "id": "123", "name": "Jane Smith" }
}

The patient demographics (id, name) are resolved from whichever source contains a Patient resource or CDA recordTarget.

Step 3: Check ingest status

Confirm that both sources have been processed and the graph is ready to query:

curl "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/ingest/status" \
  -H "X-Clinia-API-Key: <clinia-api-key>"

When ready is true, the pipeline has finished:

{
  "ready": true,
  "sources": [
    { "label": "FHIR Bundle", "stats": { "itemsScanned": 42, "entitiesExtracted": 12, "eventsExtracted": 8 } },
    { "label": "Summary_20230907.xml", "stats": { "itemsScanned": 28, "entitiesExtracted": 9, "eventsExtracted": 4 } }
  ],
  "patient": { "id": "123", "name": "Jane Smith" },
  "loadStats": { "entitiesExtracted": 18, "eventsExtracted": 11, "relationshipsExtracted": 22 },
  "loadMs": 1340
}

Notice loadStats.entitiesExtracted (18) is fewer than the sum of the two individual ingest calls (12 + 9 = 21). That gap is entity resolution working. Three entity pairs were recognised as duplicates across the FHIR and CDA sources and merged into single nodes.

Step 4: Browse the virtual file system

Navigate the resolved graph through the VFS. Start at the root to see what categories were populated:

curl "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/vfs?path=/" \
  -H "X-Clinia-API-Key: <clinia-api-key>"

Then drill into active conditions:

curl "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/vfs?path=/conditions/active" \
  -H "X-Clinia-API-Key: <clinia-api-key>"
{
  "path": "/patient/123/conditions/active",
  "type": "directory",
  "children": [
    { "name": "type_2_diabetes_mellitus", "type": "directory", "preview": "Active since 2016-03" },
    { "name": "hypertension",             "type": "directory", "preview": "Active since 2019-01" },
    { "name": "chronic_kidney_disease",   "type": "directory", "preview": "Stage 3b, active"    }
  ]
}

The path in the response uses the VFS patient ID (123) extracted from the ingested record. It is not the registry key (demo-patient) used in the request URL. These are the same value only if your FHIR bundle's Patient.id matches the registry key you chose.

Each condition slug is derived from the primary display name: lowercased, spaces and special characters replaced with underscores. If the same condition appeared in both the FHIR bundle and the CDA document, it is a single entry here, not two.

Inspect what files are available under a condition:

curl "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/vfs?path=/conditions/active/type_2_diabetes_mellitus" \
  -H "X-Clinia-API-Key: <clinia-api-key>"
{
  "path": "/patient/123/conditions/active/type_2_diabetes_mellitus",
  "type": "directory",
  "children": [
    { "name": "_story.md",  "type": "file", "preview": "longitudinal condition narrative" },
    { "name": "_raw.json",  "type": "file", "preview": "structured entity + relationships" }
  ]
}

Step 5: Read a condition story

Read the full narrative for the condition. The story assembles onset, current medications, monitoring labs, and complications from the merged entity graph:

curl "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/read?path=/conditions/active/type_2_diabetes_mellitus/_story.md" \
  -H "X-Clinia-API-Key: <clinia-api-key>"
{
  "content": "# Type 2 Diabetes Mellitus\n\n**Status:** Active since 2016-03\n**Codes:** SNOMED 44054006 · ICD-10 E11.9\n**Sources:** 3 sources (FHIR Bundle, Summary_20230907.xml, Labs_2023.xml)\n\n## Current Treatment\n| Medication | Dose | Since |\n|-----------|------|-------|\n| Metformin | 1000mg BID | 2016-04 |\n..."
}

For a shorter summary, pass format=compact:

curl "https://api.<workspace-id>.clinia.cloud/patients/demo-patient/read?path=/conditions/active/type_2_diabetes_mellitus/_story.md&format=compact" \
  -H "X-Clinia-API-Key: <clinia-api-key>"

What you built

You ingested two sources for the same patient, let the pipeline resolve and merge duplicate entities across them, and retrieved a unified clinical narrative, without writing any custom parsing logic.

The key concepts at play:

  • Registry key (demo-patient) is the identifier you control; it's used in all REST ingest and metadata routes
  • VFS patient ID (patient.id from the ingest response) is what appears in VFS paths. It comes from the ingested record and may differ from the registry key
  • Entity resolution merged duplicate conditions, medications, and labs across sources
  • VFS slugs are stable across re-ingests of the same data
  • Condition stories assemble cross-entity context (medications, labs, complications) into a single readable document

Next steps

On this page