Insights

In FHIR R4, a Bundle is a container for resources. Two Bundle types look similar on the wire but behave very differently on the server: transaction and batch. Pick the wrong one and you either lose atomicity you needed — or pay for a rollback you did not.

Same shape, different contract

Both are posted to the server base URL:

POST [base]
Content-Type: application/fhir+json

with Bundle.type set to transaction or batch, and each entry carrying a request (method + url). Sources: FHIR R4 Bundle, RESTful API — batch/transaction.

transactionbatch
Atomic?Yes — all succeed or the whole set failsNo — each entry stands alone
HTTP outcome200 + transaction-response, or 4xx/5xx with an OperationOutcome (no partial apply)200 if the batch was processed; check each entry’s response status
Response Bundle.typetransaction-responsebatch-response
Inter-entry referencesSupported (including urn:uuid: fullUrls rewritten to new ids)Creating references between new entries in the same batch is non-conformant
Conditional references (Patient?identifier=…)Allowed (transaction only)Not for resolving create-time links

Penford example

Harbor Engine’s FHIR practice ticket creates a Patient and an Encounter together. That must be a transaction: if Encounter create fails, the Patient must not linger alone.

{
 "resourceType": "Bundle",
 "type": "transaction",
 "entry": [
 {
 "fullUrl": "urn:uuid:11111111-1111-1111-1111-111111111111",
 "resource": {
 "resourceType": "Patient",
 "identifier": [{ "system": "http://penford.example/mrn", "value": "PMC-10042" }],
 "name": [{ "family": "Nguyen", "given": ["Alex"] }]
 },
 "request": { "method": "POST", "url": "Patient" }
 },
 {
 "fullUrl": "urn:uuid:22222222-2222-2222-2222-222222222222",
 "resource": {
 "resourceType": "Encounter",
 "status": "in-progress",
 "class": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB" },
 "subject": { "reference": "urn:uuid:11111111-1111-1111-1111-111111111111" }
 },
 "request": { "method": "POST", "url": "Encounter" }
 }
 ]
}

On success the server returns a Bundle with "type": "transaction-response" and per-entry response.status / location. On failure you get an OperationOutcome, not a half-applied pair.

When batch is the right tool

Use batch when entries must not depend on each other — for example, fetching several Patients by id in one round trip, or posting independent Observations that should not roll back together. The overall HTTP status can be 200 even when some entries return 404 or 422; always read entry.response.status.

Analyst checklist

  1. Need all-or-nothing clinical integrity? → transaction.
  2. Need independent ops in one HTTP call? → batch.
  3. Creating resources that reference each other with temporary UUIDs? → transaction only.
  4. Confirm the partner’s CapabilityStatement actually supports batch/transaction — many servers implement only one.

Try a sample in the free FHIR viewer (Insights explain Bundle type and entries), or practice in the Sandbox.

Keep practicing — free HL7 viewer and FHIR viewer with Insights, or the Sandbox.
Academy previewFree Sandbox