Fetching FHIR R4 Resources for OpenMed
SkillDev toolsLets your agent pull patient charts, reports, and lab results from a hospital records system and extract clinical notes.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Fetching FHIR R4 Resources for OpenMed skill
About this capability
Fetches and pages FHIR R4 resources (Patient, DocumentReference, DiagnosticReport, Observation, Condition) from a FHIR REST server, decodes base64 attachments, and extracts clinical narrative for OpenMed. Use before OpenMed processing when pulling charts from an EHR FHIR API (Epic, Cerner/Oracle, HA
What this skill tells your AI
The instructions your AI receives, as published by maziyarpanahi/openmed in skills/fetching-fhir-resources/SKILL.md and read by ahel’s review.
FHIR R4 is the modern EHR API: a RESTful, JSON-or-XML interface over resources
like Patient, Encounter, Condition, Observation, DiagnosticReport,
and DocumentReference. The unstructured clinical text you want for NLP lives
in DocumentReference.content.attachment and
DiagnosticReport.presentedForm — usually base64-encoded PDF, RTF, or
plain text. This skill pulls those resources, pages through results, decodes the
attachments, and hands the narrative to OpenMed.
When to use
- You have FHIR R4 access to an EHR (Epic, Oracle Health/Cerner, HAPI, Medplum, Azure/Google/AWS HealthLake) and want note text for de-id and NER.
- You need to page a large search result set safely (
Bundle.link[next]). - You want to pull a patient's documents/reports and rejoin NLP output by patient and encounter.
FHIR REST in one minute
Search is GET [base]/[Type]?param=value. Results come back as a
searchset Bundle; the next page is the URL in Bundle.link where
relation == "next". Use _count to size pages, _revinclude to pull related
resources in one round trip, and _since/_lastUpdated for incremental sync.
GET /Patient?identifier=http://hospital.org/mrn|12345
GET /DocumentReference?patient=Patient/abc&category=clinical-note&_count=50
GET /DiagnosticReport?patient=Patient/abc&_revinclude=Observation:related
Quick start
Page a search, decode attachments, hand narrative to OpenMed:
import base64
import requests
import openmed
BASE = "https://fhir.example.org/r4"
HEADERS = {"Accept": "application/fhir+json", "Authorization": "Bearer <token>"}
def iter_bundle(url, params=None):
"""Yield resources across all pages following Bundle.link[next]."""
while url:
bundle = requests.get(url, params=params, headers=HEADERS, timeout=30).json()
for entry in bundle.get("entry", []):
yield entry.get("resource", {})
params = None # next links are fully-qualified
url = next(
(l["url"] for l in bundle.get("link", []) if l.get("relation") == "next"),
None,
)
def attachment_text(att):
"""Decode a FHIR Attachment to text (handles base64 and inline text/plain)."""
if att.get("data"):
raw = base64.b64decode(att["data"])
if att.get("contentType", "").startswith("text/"):
return raw.decode("utf-8", "replace")
return "" # PDF/RTF: route to OpenMed multimodal/OCR intake instead
return ""
# Pull a patient's clinical notes and analyze each.
for doc in iter_bundle(f"{BASE}/DocumentReference",
{"patient": "Patient/abc",
"category": "clinical-note", "_count": 50}):
for content in doc.get("content", []):
text = attachment_text(content.get("attachment", {}))
if not text.strip():
continue
deid = openmed.deidentify(text, method="replace", policy="hipaa_safe_harbor")
result = openmed.analyze_text(deid.text, output_format="dict")
patient_ref = doc.get("subject", {}).get("reference") # rejoin key
Workflow
- Authenticate. Most production FHIR endpoints use SMART-on-FHIR OAuth2
(client-credentials for backend services). Scope to the minimum
(
system/DocumentReference.read,system/DiagnosticReport.read). - Search narrowly. Filter by
patient,category,type(LOINC),date, and_count. Prefer server-side filtering over client-side. - Page via
Bundle.link[next]until exhausted. Never assume one page. - Extract narrative:
DocumentReference.content.attachmentandDiagnosticReport.presentedForm. Decode base64; for PDF/RTF/scanned content, route bytes to OpenMed's document intake (multimodal/ocr) rather than decoding as UTF-8. - De-identify → analyze each narrative with OpenMed.
- Rejoin results to
subject.reference(patient) andcontext.encounterso downstream consumers can group by patient/encounter — storing hashed, not raw, identifiers.
Hand-off to / from OpenMed
-
To OpenMed (client-side): decoded narrative →
openmed.deidentify→openmed.analyze_text. Carrysubject.referenceas the rejoin key. -
Server-side
$de-identify:openmed.interop.fhir_operationsimplements the FHIR$de-identifyoperation logic over the OpenMed privacy pipeline:de_identify_resource(resource, policy=..., method=...)de_identify_bundle(bundle, policy=..., method=...)de_identify(parameters)— accepts/returns aParametersenvelope and reports modified element paths as anOperationOutcome. It de-identifies free-text strings, identifier values, andtext.divnarrative while never altering codes, references, systems, or temporal values. Use this to de-identify a whole fetched Bundle before storage:
from openmed.interop.fhir_operations import de_identify_bundle safe_bundle = de_identify_bundle(bundle, policy="hipaa_safe_harbor", method="replace") -
Onward: re-export structured findings with
openmed.clinical.exporters.fhir(to_bundle,to_operation_outcome).
Edge cases & gotchas
- Attachments are often base64.
attachment.datais base64; large files useattachment.url(a separate Binary fetch) instead. Handle both. - Non-text content types.
application/pdf,text/rtf, scanned TIFF — do notutf-8decode these; send bytes to OpenMed multimodal/OCR intake. - Pagination loops. Some servers emit cyclic or stale
nextlinks; cap page count and dedupe by resourceid. _revincludevs_include._includepulls referenced resources;_revincludepulls resources that reference yours. Mixing them changes Bundle entrysearch.mode(matchvsinclude) — filter on it.- Versioning & profiles. Confirm the server is R4 (
/metadataCapabilityStatement) and US Core-conformant; field cardinality differs across FHIR versions. - Throttling. Respect
429/Retry-After; batch with_countand back off. - PHI everywhere. A FHIR resource is PHI by definition — never log raw resources; de-identify before persistence or analytics.
Standards & references
- FHIR R4 specification: https://hl7.org/fhir/R4/
- FHIR RESTful API & search: https://hl7.org/fhir/R4/http.html and https://hl7.org/fhir/R4/search.html
- US Core Implementation Guide: https://hl7.org/fhir/us/core/
- DocumentReference: https://hl7.org/fhir/R4/documentreference.html
- DiagnosticReport (
presentedForm): https://hl7.org/fhir/R4/diagnosticreport.html - SMART on FHIR (backend services auth): https://hl7.org/fhir/smart-app-launch/
Signals
- GitHub stars
- 5k
- Forks
- 666
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
fetching-fhir-resources- Source
- github.com/maziyarpanahi/openmed