SMART on FHIR

Creating Data

Note: Opala’s publicly documented APIs are read-only. The guidance and examples on this page describe standard FHIR behavior on a Firely-based server for reference. Write operations and administrative server behaviors are not available on public endpoints.

Transactions and Submitting Bundles

There are three distinct ways to POST bundles:

  • POST /Bundle — store a Bundle resource (e.g., document, collection); no operational processing.

  • POST / with Bundle.type=”transaction” — execute an atomic, all-or-nothing set of operations.

  • POST / with Bundle.type=”batch” — execute best-effort independent operations (not atomic).

Posting a Bundle as a resource

Use this when you want to store a Bundle itself (e.g., document, collection) – not execute operations.

Endpoint:

POST https://{baseurl}/Bundle
Behavior:
  • The server stores the submitted bundle of data as a Bundle resource.

  • Do not send the Bundle.type of transaction or batch to /Bundle; that indicates the wrong endpoint for operational processing.
Example:

{
    "resourceType": "Bundle",
    "type": "document",
    "entry": [
        {
	  "resource": {
	    "resourceType": "Composition",
	    "status": "final",
	    "type": { "coding": [ { "system": "http://loinc.org", "code": "34133-9" } ] }
			}
		}
	]
}

FHIR Transaction (atomic “all-or-nothing”)

Use this when you need to create/update multiple resources atomically.

Endpoint:
POST https://{baseurl}
Bundle requirement: Bundle.type = “transaction”
Behavior:
  • All entries are processed as a single unit; any failure rolls back the entire bundle.

  • Entries may create, update, delete, or search.

  • Cross-entry references should use entry.fullUrl URNs (standard FHIR pattern).

  • Conditional create upsert is supported via request.ifNoneExist.
Example (atomic Observation + Patient via URN link + conditional create):

{
   "resourceType": "Bundle",
   "type": "transaction",
   "entry": [
      {
	"fullUrl": "urn:uuid:patient-123",
	"resource": {
	     "resourceType": "Patient",
	     "identifier": [
		{ "system": "https://acme.org/mrns", "value": "12345" }
	     ],
	     "name": [
		{ "family": "Jameson", "given": [ "J", "Jonah" ] }
	     ],
	     "gender": "male"
	},
	"request": {
	     "method": "POST",
	     "url": "Patient",
	     "ifNoneExist": "identifier=https://acme.org/mrns|12345"
	}
     },
     {
	"resource": {
	     "resourceType": "Observation",
	     "status": "final",
	     "code": {
		"coding": [
		   {
		     "system": "http://loinc.org",
		     "code": "789-8",
		     "display": "Erythrocytes [#/volume] in Blood by Automated count"
		   }
		]
	     },
	     "subject": { "reference": "urn:uuid:patient-123" },
	     "valueQuantity": {
		"value": 4.12,
		"unit": "10 trillion/L",
		"system": "http://unitsofmeasure.org",
		"code": "10*12/L"
		}
	},			
        "request": { "method": "POST", "url": "Observation" }
        }
    ]
}


Notes:

  • ifNoneExist performs a conditional create (“upsert”) using a standard search expressions.

  • Using fullUrl + URN keeps the graph consistent within the atomic transaction.

FHIR Batch (best-effort, not atomic)

Use this for independent operations that shouldn’t roll back together.

Endpoint:
POST https://{baseurl}
Bundle requirement: Bundle.type = “batch”

Behavior:

  • Each entry is processed as a separate operation (separate DB transactions).

  • The response Bundle includes per-entry status outcomes.

  • The entire bundle must still be valid FHIR (no malformed datatypes, etc.).
Example (two independent deletes):

{
     "resourceType": "Bundle",
     "type": "batch",
     "entry": [
	{ "request": { "method": "DELETE", "url": "Organization/1" } },
	{ "request": { "method": "DELETE", "url": "Organization/2" } }
     ]
}

Reference Targets When Order Is Unpredictable

When loading data from multiple sources, references may point to resources that haven’t been created yet (e.g., an Observation referencing a Patient). You have three patterns to handle this. Pattern A is pure FHIR and recommended; Patterns B–C are server-specific and are included as reference-only for environments that enable them.

A) Transaction with Conditional Create (recommended, standard FHIR)

Use a single transaction bundle and “upsert” the target using request.ifNoneExist. Link intra-bundle resources with entry.fullUrl URNs.

Behavior:

  • All entries are processed as one atomic unit; any failure rolls back the whole bundle.

  • ifNoneExist performs a conditional create using a standard search expression (e.g., identifier=system|value).

  • entry.fullUrl URNs let other entries reference the newly created resource.

Example (atomic Patient + Observation via URN link):

{
     "resourceType": "Bundle",
     "type": "transaction",
     "entry": [
          {
	     "fullUrl": "urn:uuid:patient-123",
	     "resource": {
	          "resourceType": "Patient",
	          "identifier": [
		     { "system": "https://acme.org/mrns", "value": "12345" }
	          ],
	          "name": [
		     { "family": "Jameson", "given": [ "J", "Jonah" ] }
	          ],
	          "gender": "male"
	    },
	    "request": {
	          "method": "POST",
	          "url": "Patient",
	          "ifNoneExist": "identifier=https://acme.org/mrns|12345"
    	    }
          },
          {			
            "resource": {
	          "resourceType": "Observation",
	          "status": "final",
	          "code": {
	 	    "coding": [
		          {
			    "system": "http://loinc.org",
			    "code": "789-8",
			    "display": "Erythrocytes [#/volume] in Blood by Automated count"
		          }
	              ]
	          },
	          "subject": { "reference": "urn:uuid:patient-123" },
	          "valueQuantity": {
	          	"value": 4.12,
	         	"unit": "10 trillion/L",
		        "system": "http://unitsofmeasure.org",
		        "code": "10*12/L"
		   }
	       },
	    "request": { "method": "POST", "url": "Observation" }
          }	
     ]
}

B) Inline Match-URL References (reference-only; server-specific)

Some servers allow a Reference.reference to contain a match URL (e.g., Patient?identifier=system|value). The server treats it as a local search and either links to the match or creates a new target.

Status on Opala public APIs: not exposed. If enabled in a managed environment, the typical behavior is:

  • 0 results: create the target and replace the match URL with a concrete reference.

  • 1 result: replace with the found target.

  • >1 results: operation fails (ambiguous).

Example (reference-only):

{
	"resourceType": "Observation",
	"status": "final",
	"code": { "coding": [ { "system": "http://loinc.org", "code": "789-8" } ] },
	"subject": {
		"reference": "Patient?identifier=https://foo|1234",
		"identifier": { "system": "https://foo", "value": "1234" }
	},
	"valueQuantity": {
		"value": 4.12,
		"system": "http://unitsofmeasure.org",
		"code": "10*12/L"
	}
}


Note on Reference.identifier: If both the match URL and Reference.identifier are present, servers may add one or both identifiers to the created target; rules vary by implementation. This pattern is not standard FHIR behavior and should be used only where explicitly supported.

C) Auto-Creating Placeholder Targets (reference-only; vendor-specific)

Some stacks support auto-creating an empty resource when you reference a non-existent ID (e.g., Patient/ABC). This is not part of core FHIR and isn’t exposed on Opala’s public APIs.

Typical behaviors (if enabled)

  • A minimal resource is created with the referenced ID.

Example (reference-only):

{
	"resourceType": "Observation",
	"status": "final",
	"code": { "coding": [ { "system": "http://loinc.org", "code": "789-8" } ] },
	"subject": { "reference": "Patient/ABC" },
	"valueQuantity": {
		"value": 4.12,
		"system": "http://unitsofmeasure.org",
		"code": "10*12/L"
	}
}

Cautions

  • Placeholders can leak partial/assumed state and complicate reconciliation.

  • If placeholders are created without an identifier, conditional updates later may be impossible; always prefer pattern A when you can.

Import / Mass-Ingestion Considerations

The following are general best practices for large loads (reference-only; specifics vary by environment):

  • Prefer transactions for graph integrity, but keep bundles to a reasonable size to avoid timeouts and memory pressure.

  • Use conditional create/update to deduplicate by business identifiers.

  • Pre-validate resources and value sets where possible (e.g., $validate), and normalize codes/URIs ahead of time.

  • Avoid server-specific features (inline match URLs, placeholder auto-create) unless your environment explicitly supports them.

  • Coordinate with Opala for managed environments; performance tuning (threading, DB, indexing) is handled at the platform level.