Batch vs Transaction Bundles: Why the Validation Rules Feel Different

Editorial illustration in woodblock-print style depicting the contrast between a batch Bundle of independent entries and a transaction Bundle committing atomically

FHIR Bundles come in a handful of types, but the two that most integration teams touch every day are batch and transaction. On the surface they look nearly identical, and that similarity is exactly why validation rules that pass for one can fail confusingly for the other. The differences are small in the schema and large in the semantics.

The short answer is that a batch is a bag of independent operations while a transaction is a single atomic unit, and validators care about that distinction. For the surrounding context on why validation matters, what really breaks when you skip Bundle validation is the primer, and the FHIR learning path points at the rest of the series once you have that grounding. This piece stays on the comparison itself.

What the Two Types Actually Promise

A batch Bundle promises nothing about atomicity. The receiver processes each entry independently. If entry three fails, entries one, two, four, and five still commit. You get back a response Bundle with the same number of entries, and each one carries its own status.

A transaction Bundle promises all-or-nothing. Either every entry succeeds and the whole thing commits, or the first failure rolls back everything. The receiver has to plan the write order to respect references, and any single failure aborts the run.

That semantic gap is why the validation rules diverge. Structural validation checks the same shape on both, but transaction-only rules kick in once Bundle.type is transaction.

Where the Rules Diverge

The most visible difference is entry.request. In a transaction, every entry needs a request element with an HTTP verb and a URL. The verb tells the receiver what to do with the resource (create, update, delete), and the URL disambiguates targets when references overlap. In a batch, request is expected too, but there is no cross-entry dependency.

Reference resolution is the second difference. A transaction Bundle can use urn:uuid: references to point at resources being created within the same Bundle. The receiver rewires those references at commit time. A batch does not get this rewiring; each entry stands alone and its references must resolve to already-persisted resources.

Contrast layout: a batch flow with independent entries committing separately on the left, and a transaction flow committing all together or rolling back on the right

Common Confusion Patterns

The mistake we see most is a transaction Bundle with urn:uuid: fullUrls but no entry.request elements. Structurally each entry looks valid; the validator flags the missing request fields as required. The natural fix is often to set Bundle.type to collection if what you actually meant was "a group of resources with no execution semantics".

The opposite mistake is a batch Bundle that references urn:uuid: values across entries and expects them to resolve. They will not. A validator with reference resolution enabled catches it; without that, the Bundle passes locally and fails at ingest.

For the wider list of these traps, the 5 structural mistakes in real-world Bundles walks through what falls out of these mismatches. And the site's Bundle validator will flag both patterns on the first pass, which is usually enough to send you back to the generator.

How to Pick Between Them

Choose transaction when the payload only makes sense committed as a whole: a patient plus their initial encounter plus its observations, none of which should exist without the others. Choose batch when the entries are truly independent, like a nightly export of new referrals.

The wrong choice is not just a modelling issue. It changes what the receiver logs when something fails, what the retry story looks like, and how a Bundle that passes locally but the receiver rejects will surface downstream. Picking correctly is often the shortest path to a Bundle that behaves the same everywhere.

Sources

  • HL7 FHIR R5 Bundle Spec - HL7 FHIR R5 canonical Bundle specification covering types, entry.request semantics, and batch vs transaction processing rules