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 for a patient
  2. Ingest a CDA document for the same patient
  3. Check ingest status and confirm entity resolution ran
  4. Browse the virtual file system to discover what was extracted
  5. 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.

Time: ~15 minutes.

Prerequisites

  • A Patient Memory workspace with a workspace ID and an OAuth access token. See Manage Credentials for how to obtain one. Replace <workspace-id> and <access-token> in the commands below.

Steps


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.

No PHI? Use the synthetic bundle

Download the

Jeanne Tremblay synthetic FHIR R4 bundle

to follow along without real patient data. It covers 10 encounters spanning 2017–2026: COPD GOLD stage 3, hypertension, dyslipidemia, knee osteoarthritis, and an acute COPD exacerbation.

curl -X POST "https://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/ingest/fhir" \
  -H "Authorization: Bearer <access-token>" \
  -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": "jeanne-72f-copd", "name": "Jeanne Tremblay" }
}

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.

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://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/ingest/cda" \
  -H "Authorization: Bearer <access-token>" \
  -H "Content-Type: text/plain" \
  --data-binary @summary.xml
{
  "ok": true,
  "source": "cda",
  "stats": { "itemsScanned": 28, "entitiesExtracted": 9, "eventsExtracted": 4 },
  "warnings": 1,
  "patient": { "id": "jeanne-72f-copd", "name": "Jeanne Tremblay" }
}

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

Check ingest status

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

curl "https://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/ingest/status" \
  -H "Authorization: Bearer <access-token>"

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": "jeanne-72f-copd", "name": "Jeanne Tremblay" },
  "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.

Browse the virtual file system

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

curl "https://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/vfs?path=/" \
  -H "Authorization: Bearer <access-token>"

Then drill into active conditions:

curl "https://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/vfs?path=/conditions/active" \
  -H "Authorization: Bearer <access-token>"
{
  "path": "/patient/jeanne-72f-copd/conditions/active",
  "type": "directory",
  "children": [
    {
      "name": "chronic_obstructive_lung_disease",
      "type": "directory",
      "preview": "GOLD stage 3, active since 2014"
    },
    { "name": "hypertension", "type": "directory", "preview": "Active since 2011" },
    { "name": "dyslipidemia", "type": "directory", "preview": "Active since 2013" }
  ]
}

The path in the response uses the VFS patient ID (jeanne-72f-copd) extracted from the ingested record. It is not the registry key (jeanne-tremblay) 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://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/vfs?path=/conditions/active/type_2_diabetes_mellitus" \
  -H "Authorization: Bearer <access-token>"
{
  "path": "/patient/jeanne-72f-copd/conditions/active/chronic_obstructive_lung_disease",
  "type": "directory",
  "children": [
    { "name": "_story.md", "type": "file", "preview": "longitudinal condition narrative" },
    { "name": "_raw.json", "type": "file", "preview": "structured entity + relationships" }
  ]
}

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://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/read?path=/conditions/active/type_2_diabetes_mellitus/_story.md" \
  -H "Authorization: Bearer <access-token>"
{
  "content": "# Chronic Obstructive Lung Disease\n\n**Status:** Active since 2014, GOLD stage 3\n**Codes:** SNOMED 13645005 · ICD-10 J44.1\n**Sources:** 2 sources (FHIR Bundle, Spirometry_2025.xml)\n\n## Current Treatment\n| Medication | Dose | Since |\n|-----------|------|-------|\n| Tiotropium | 18mcg daily | 2015 |\n| Budesonide/Formoterol | 200/6mcg BID | 2018 |\n| Albuterol | 100mcg PRN | 2014 |\n..."
}

For a shorter summary, pass format=compact:

curl "https://<workspace-id>.w.clinia.cloud/v1/patients/demo-patient/read?path=/conditions/active/type_2_diabetes_mellitus/_story.md&format=compact" \
  -H "Authorization: Bearer <access-token>"

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 (jeanne-tremblay) 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