Skip to main content
fi-fhir docs

FHIR Output

fi-fhir maps canonical events to FHIR R4 resources with US Core profile annotations. Validation is optional on workflow actions; the pinned structural checks do not establish full FHIR conformance. See the conformance matrix for measured coverage.

Events delivered by FHIR actions

Both the legacy workflow action and the durable FHIR destination use the shared projector:

Event typeResources delivered
patient_admit, patient_transfer, patient_update, patient_dischargePatient and Encounter
lab_resultDiagnosticReport and Observations
vital_signObservation
conditionCondition
procedureProcedure
immunizationImmunization
medication_requestMedicationRequest
allergy_intoleranceAllergyIntolerance

Other pkg/fhir mapper methods remain available to library callers, but their event types are not yet connected to these delivery paths. Unsupported events return an error; JSON events no longer silently become Patient-only output.

The six clinical event types and lab results reference an existing Patient. They do not overwrite demographics from a partial clinical document. Patient and Encounter references use qualified identifiers; create those resources first, with the same source or assigning-authority system. Other references (such as Practitioner and Location) retain the mapper's literal IDs and require matching resources on the destination.

Selecting workflow resources

Set resource: Patient to create only a Patient from an admission, or omit resource to deliver every projected resource. A selection that does not match any output fails before sending. A DiagnosticReport-only selection also fails if it would omit its referenced Observations.

workflow:
  name: patient-demographics
  version: "1.0"
  routes:
    - name: patient
      filter:
        event_type: patient_admit
      actions:
        - type: fhir
          endpoint: https://fhir.example.org/r4
          resource: Patient
          validate_fhir: true

A selected Patient uses a direct POST unless bundle: true is set. Other outputs use a transaction so the server can resolve references. Bundle entries have fullUrl values, internal references point to those values, and external Patient/Encounter references use conditional identifier searches. These workflow creates use POST and can duplicate resources on retry. For persisted, retry-safe delivery, use the durable FHIR destination.

Durable clinical writes use the canonical event ID under urn:fi-fhir:event:<source>:<event_type>. Re-delivering a stored event updates the same resource; a new event ID creates a separate record, including a correction submitted as a new event. Source message IDs are not used as clinical record keys because one document can contain many records. Missing source, event ID, or patient identifier prevents durable delivery.

Patient Resource

Maps from patient events (patient_admit, patient_update).

Field Mapping

Event FieldFHIR PathNotes
patient.mrnidentifier[0].valueMRN identifier
patient.ssnidentifier[1].valueSSN (if not redacted)
patient.name.familyname[0].familyFamily name
patient.name.givenname[0].givenGiven names array
patient.birthDatebirthDateFHIR date format
patient.gendergendermale, female, other, unknown
patient.addressaddress[0]Full address structure
patient.phonetelecom[0]Phone contact point
patient.emailtelecom[1]Email contact point
patient.raceextensionUS Core Race extension
patient.ethnicityextensionUS Core Ethnicity extension

Example Output

{
  "resourceType": "Patient",
  "id": "patient-mrn12345",
  "meta": {
    "profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient"]
  },
  "identifier": [
    {
      "type": {
        "coding": [{
          "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
          "code": "MR"
        }]
      },
      "system": "urn:oid:1.2.3.4.5.6",
      "value": "MRN12345"
    }
  ],
  "name": [{
    "use": "official",
    "family": "Smith",
    "given": ["John", "A"]
  }],
  "gender": "male",
  "birthDate": "1980-01-15",
  "address": [{
    "use": "home",
    "line": ["123 Main St"],
    "city": "Springfield",
    "state": "IL",
    "postalCode": "62701"
  }]
}

Encounter Resource

Maps from admit/discharge/transfer events.

Field Mapping

Event FieldFHIR PathNotes
encounter.identifieridentifier[0].valueVisit number. The durable fhir transport qualifies a value sent with no system under urn:fi-fhir:source:<source>
encounter.classclassinpatient, outpatient, emergency
encounter.classtypeDerived from the patient class (PV1-2); see below
encounter.statusstatusin-progress, finished, etc.
encounter.period.startperiod.startAdmit datetime
encounter.period.endperiod.endDischarge datetime
encounter.locationlocation[0].locationLocation reference
encounter.providerparticipantAttending physician

Encounter Class Mapping

Event ClassFHIR Class Code
inpatientIMP
outpatientAMB
emergencyEMER
observationOBSENC

Encounter Type

Encounter.type is required by US Core. The only encounter-kind fact the event carries is the patient class, so the type is derived from it:

Patient class (PV1-2)type.coding
I inpatientSNOMED CT 86181006 Evaluation and management of inpatient, and HL7 v2 table 0004 I
E emergencySNOMED CT 4525004 Emergency department patient visit, and HL7 v2 table 0004 E
O, P, R, B, C, N, UHL7 v2 table 0004 code only
any other valuenone; the value is sent as type.text
absentDataAbsentReason unknown

Observation Resource (Laboratory)

Maps from lab_result events.

Field Mapping

Event FieldFHIR PathNotes
observation.codecodeLOINC coding
observation.valuevalue[x]Quantity, string, or CodeableConcept
observation.unitvalueQuantity.unitUCUM unit
observation.referenceRangereferenceRangeNormal range
observation.interpretationinterpretationH, L, A, N, etc.
observation.statusstatusfinal, preliminary, etc.
observation.effectiveDateTimeeffectiveDateTimeSpecimen collection time
observation.issuedissuedResult available time

Example Output

{
  "resourceType": "Observation",
  "meta": {
    "profile": ["http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab"]
  },
  "status": "final",
  "category": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/observation-category",
      "code": "laboratory"
    }]
  }],
  "code": {
    "coding": [{
      "system": "http://loinc.org",
      "code": "2345-7",
      "display": "Glucose [Mass/volume] in Serum or Plasma"
    }]
  },
  "subject": {
    "reference": "Patient/patient-mrn12345"
  },
  "effectiveDateTime": "2024-01-15T10:30:00Z",
  "valueQuantity": {
    "value": 95,
    "unit": "mg/dL",
    "system": "http://unitsofmeasure.org",
    "code": "mg/dL"
  },
  "interpretation": [{
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
      "code": "N",
      "display": "Normal"
    }]
  }]
}

Observation Resource (Vital Signs)

Maps from vital_sign events to specific US Core Vital Signs profiles.

Supported Vital Signs

Vital SignLOINC CodeUS Core Profile
Blood Pressure85354-9us-core-blood-pressure
BMI39156-5us-core-bmi
Body Height8302-2us-core-body-height
Body Weight29463-7us-core-body-weight
Body Temperature8310-5us-core-body-temperature
Heart Rate8867-4us-core-heart-rate
Respiratory Rate9279-1us-core-respiratory-rate
Pulse Oximetry2708-6us-core-pulse-oximetry

DiagnosticReport Resource

Groups related observations for lab panels.

Field Mapping

Event FieldFHIR PathNotes
diagnosticReport.codecodePanel/test code
diagnosticReport.statusstatusfinal, preliminary
diagnosticReport.categorycategoryLAB category
diagnosticReport.effectiveDateTimeeffectiveDateTimeCollection time
diagnosticReport.resultsresultReferences to Observations

Claim Resource

Maps from claim_submitted events (EDI 837P/837I).

Field Mapping

Event FieldFHIR PathNotes
claim.identifieridentifierClaim control number
claim.statusstatusactive, draft
claim.typetypeinstitutional, professional
claim.patientpatientPatient reference
claim.providerproviderBilling provider
claim.diagnosisdiagnosisICD-10 codes
claim.procedureprocedureCPT/HCPCS codes
claim.totaltotalClaim total amount

FHIR Bundles

Multiple resources are grouped into bundles:

Transaction Bundle

{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "fullUrl": "urn:uuid:patient-1",
      "resource": { /* Patient */ },
      "request": {
        "method": "PUT",
        "url": "Patient/mrn12345"
      }
    },
    {
      "fullUrl": "urn:uuid:encounter-1",
      "resource": { /* Encounter */ },
      "request": {
        "method": "POST",
        "url": "Encounter"
      }
    }
  ]
}

Batch Bundle

Same structure, but type: "batch" for independent operations.

Validation

Enable Validation

actions:
  - type: fhir
    endpoint: https://fhir.example.com
    validate_fhir: true              # Validate before sending

Validation Levels

  • Structural: JSON schema validation
  • Profile: US Core profile conformance
  • Business Rules: Required fields, code systems

CLI Validation

# Validate a FHIR resource
fi-fhir fhir validate patient.json

# Validate a bundle
fi-fhir fhir validate bundle.json --profile us-core

Authentication

Action configuration is a flat map of string values. Nested YAML blocks under an action are ignored — always use the flat keys shown below. Values are also literal: ${VAR} references are not expanded by fi-fhir. Render the file before loading it if you need environment-specific secrets:

envsubst < workflow.yaml.tmpl > workflow.yaml

OAuth2 Client Credentials

actions:
  - type: fhir
    endpoint: https://fhir.example.com/r4
    token_url: https://auth.example.com/oauth2/token
    client_id: my-client-id
    client_secret: my-client-secret
    scopes: system/Patient.read,system/Patient.write,system/Encounter.write

token_url, client_id, and client_secret must all be present; if any is missing, OAuth2 is skipped and the action falls back to the static token below. scopes is a single string — separate multiple scopes with commas or spaces.

Static Bearer Token

actions:
  - type: fhir
    endpoint: https://fhir.example.com/r4
    token: my-static-bearer-token        # Sends "Authorization: Bearer <token>"
    # authorization: "Basic ..."         # Or set the Authorization header verbatim

Token Caching

OAuth2 tokens are cached and automatically refreshed before expiration.

401 Handling

If a 401 is received:

  1. Token is refreshed
  2. Request is retried
  3. If still failing, error is returned

Batch Operations

When an event produces more than one resource, they are sent as a single FHIR transaction bundle automatically. Set bundle to force a transaction bundle even for a single resource:

actions:
  - type: fhir
    endpoint: https://fhir.example.com/r4
    token: my-static-bearer-token
    bundle: "true"                   # Send as a transaction bundle
    timeout: 30s                     # Request timeout (default: 30s)

Bundle size is not configurable — a bundle carries exactly the resources produced by the event being processed.

US Core Profile Compliance

All generated resources include:

  1. Profile declaration in meta.profile
  2. Must-Support elements populated when available
  3. Required extensions (race, ethnicity for Patient)
  4. Standard terminologies (LOINC, SNOMED, ICD-10)

Checking Compliance

# Generate FHIR from event
fi-fhir parse sample.hl7 | fi-fhir fhir generate --profile us-core

# Validate against US Core
fi-fhir fhir validate output.json --profile us-core

Custom Mappings

Override default mappings in Source Profile:

fhirMapping:
  targetVersion: R4
  bundleType: transaction

  resourceMappings:
    - event_type: patient_admit
      resources: [Patient, Encounter]
      customFields:
        - source: patient.custom_field
          target: Patient.extension
          extension_url: http://example.com/fhir/extension

See Also

FHIR Output | fi-fhir docs