Assembling FHIR Bundles
SkillDev toolsLets your agent package medical records into a single FHIR bundle ready to send to an EHR system.
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 Assembling FHIR Bundles skill
About this capability
Package multiple FHIR R4 resources produced from OpenMed output into a single valid transaction Bundle ready to POST to an EHR, using OpenMed's verified bundle assembler openmed.clinical.exporters.fhir.to_bundle. Covers deterministic urn:uuid fullUrls, automatic in-Bundle reference rewriting, reques
What this skill tells your AI
The instructions your AI receives, as published by maziyarpanahi/openmed in skills/assembling-fhir-bundles/SKILL.md and read by ahel’s review.
A FHIR server ingests one transaction Bundle, not loose resources, and the
resources inside it must cross-reference each other (Condition.subject →
Patient, Observation.encounter → Encounter, DiagnosticReport.result →
Observation). OpenMed ships a deterministic, mechanical Bundle assembler —
openmed.clinical.exporters.fhir.to_bundle — that wraps the resources you built
in exporting-to-fhir into a valid R4 Bundle and wires up the references.
When to use
Use after you have a list of standalone resources from exporting-to-fhir and
the destination is a FHIR server. Reach for it when the user says "build a
Bundle", "transaction", "POST these resources", or needs internal references
resolved. To check the Bundle against US Core, hand off to validating-us-core.
What OpenMed gives you (verified API)
from openmed.clinical.exporters.fhir import to_bundle, deterministic_fullurl
bundle = to_bundle(
resources, # Sequence[Mapping] each with a resourceType
doc_id="note-2024-03-02-001", # seeds stable urn:uuid fullUrls
bundle_type="transaction", # "transaction" | "batch" | "collection" | ...
)
to_bundle does exactly three things, and never synthesises or validates:
- Deterministic
fullUrl. Each resource gets aurn:uuidseeded bydoc_id+ its index, so the same input always produces byte-identical output (golden-test friendly). You can pre-compute the same urn withdeterministic_fullurl(doc_id, index). - Reference rewriting. Any
{"reference": "ResourceType/id"}whose target is present in the Bundle is repointed at that resource'sfullUrl. References to resources absent from the Bundle (e.g. a Patient removed by de-identification) are left untouched — no dangling internal refs. - Request blocks. For
transaction/batchbundles each entry gets arequestblock ({"method": "POST", "url": "<ResourceType>"}) so the server knows to create it.
It raises ValueError if a resource lacks resourceType, or if two
resources share the same ResourceType/id (duplicate ids would silently corrupt
the reference map).
Quick start
from openmed.clinical.exporters.fhir import to_bundle
from openmed.clinical.exporters.codeable_concept_simple import coding, codeable_concept
condition = {
"resourceType": "Condition", "id": "cond-1",
"clinicalStatus": {"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
"code": "active"}]},
"code": codeable_concept(
[coding("snomed", "44054006", "Diabetes mellitus type 2")],
text="type 2 diabetes"),
"subject": {"reference": "Patient/patient-1"}, # internal ref, rewritten
}
medication = {
"resourceType": "MedicationStatement", "id": "med-1", "status": "active",
"medicationCodeableConcept": codeable_concept(
[coding("rxnorm", "860975", "metformin 500 MG Oral Tablet")],
text="metformin 500 mg"),
"subject": {"reference": "Patient/patient-1"},
}
patient = {
"resourceType": "Patient", "id": "patient-1",
"gender": "unknown", # de-identified, synthetic
}
bundle = to_bundle([patient, condition, medication],
doc_id="demo-note", bundle_type="transaction")
Worked: the transaction Bundle
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:6f1c...e2",
"resource": { "resourceType": "Patient", "id": "patient-1", "gender": "unknown" },
"request": { "method": "POST", "url": "Patient" }
},
{
"fullUrl": "urn:uuid:9a3b...77",
"resource": {
"resourceType": "Condition", "id": "cond-1",
"subject": { "reference": "urn:uuid:6f1c...e2" }
},
"request": { "method": "POST", "url": "Condition" }
},
{
"fullUrl": "urn:uuid:c0d4...19",
"resource": {
"resourceType": "MedicationStatement", "id": "med-1",
"subject": { "reference": "urn:uuid:6f1c...e2" }
},
"request": { "method": "POST", "url": "MedicationStatement" }
}
]
}
Note Condition.subject and MedicationStatement.subject were rewritten from
"Patient/patient-1" to the Patient entry's fullUrl — that is what makes the
transaction resolvable in a single POST.
Workflow
- Build resources with
exporting-to-fhir; give each a uniqueid. - Put any resource you reference inside the same Bundle — including the
Patient — so the reference resolves. References to resources you intend to be
already on the server (e.g. an existing Patient) are left as literal
"Patient/<id>"; resolve those with conditional create (below). - Call
to_bundle(resources, doc_id=<stable>, bundle_type="transaction"). - POST the whole Bundle to the server base:
POST [base] {Bundle}. - Validate first against US Core (
validating-us-core).
Conditional create (don't duplicate an existing Patient)
to_bundle writes POST <ResourceType> request blocks. To make a transaction
idempotent, post-process the entry's request to add an ifNoneExist query
so the server reuses an existing match instead of creating a duplicate:
for entry in bundle["entry"]:
if entry["resource"]["resourceType"] == "Patient":
entry["request"]["ifNoneExist"] = "identifier=http://hospital.example|MRN-REDACTED"
The server creates the Patient only if no match exists; otherwise it links the
references to the existing one. PUT with a known id is the alternative for
true upserts.
Hand-off to / from OpenMed
- From OpenMed: the resource list comes from
exporting-to-fhir, which in turn comes fromopenmed.analyze_text. Keepdoc_idstable per source document so re-running the pipeline yields the same Bundle. - De-identify a built Bundle:
openmed.interop.fhir_operations.de_identify_bundle(bundle)walks every entry's free text + XHTML narrative and de-identifies it while preserving Bundletype, entry order,fullUrls,requestblocks, and references — codes, systems, and temporal values are never altered. Use it as a final safety pass before transmission if any narrative might carry PHI. - OperationOutcome: a transaction either fully succeeds or fully fails; the
server returns a Bundle of responses (or an
OperationOutcomeon error). Surface those to the user; for your own pre-flight findings useto_operation_outcome(...)from the same package.
Edge cases & gotchas
- Resources without an
idare valid but unreferenceable — nothing can point at them and they will not be reference-rewrite targets. - Duplicate
ResourceType/idraises. This is intentional: a duplicate id would silently overwrite an entry in the reference map and corrupt cross-references. Make ids unique. - External references are left alone. Only references whose target is in
the Bundle are rewritten; a
"Patient/existing-123"you mean to resolve on the server stays literal — pair it withifNoneExistor aPUT. transactionvsbatch.transactionis atomic (all-or-nothing, server resolvesurn:uuidreferences);batchis independent per-entry and does not guarantee reference resolution. Usetransactionwhen entries reference each other.collection/documentbundles get norequestblocks (onlytransaction/batchdo) — correct, since they are not meant to be POSTed for creation.- The assembler does not validate profiles. Run
validating-us-corebefore submission.
Standards & references
- FHIR R4 Bundle: https://hl7.org/fhir/R4/bundle.html
- Transactions & batches: https://hl7.org/fhir/R4/http.html#transaction
- Bundle references /
fullUrlresolution: https://hl7.org/fhir/R4/bundle.html#references - Conditional create (
ifNoneExist): https://hl7.org/fhir/R4/http.html#cond-update
Signals
- GitHub stars
- 5k
- Forks
- 668
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
assembling-fhir-bundles- Source
- github.com/maziyarpanahi/openmed