Skip to main content
fi-fhir docs

Why Source Profiles?

Source Profiles

Source Profiles are the heart of fi-fhir's configuration system. Each profile defines how a specific data feed should be parsed, validated, and transformed.

Why Source Profiles?

Traditional integration approaches use a one-size-fits-all parser. When edge cases appear, developers add if-statements and flags until the code becomes unmaintainable.

fi-fhir inverts this pattern: the profile is the unit of scalability.

Each data feed gets its own profile, allowing:

  • Feed-specific tolerance rules
  • Custom identifier mappings
  • Different event classification logic
  • Independent validation requirements

Profile Structure

A complete Source Profile has five main sections:

# Metadata
name: epic_adt
version: '1.0'
description: 'Epic ADT interface for main hospital'

# Phase 1: Byte handling
encoding:
  charset: UTF-8
  lineEnding: auto
  bomHandling: strip

# Phase 2: Message structure
syntax:
  hl7Version: '2.5'
  fieldSeparator: '|'
  encodingChars: "^~\\&"
  escapeSequences:
    enabled: true
  strictMode: false

# Phase 3: Business logic
semantics:
  messageTypes: [ADT]
  eventTypes: [A01, A02, A03, A08]
  patientIdentifiers:
    - source_field: PID.3.1
      identifier_type: MRN
      assigning_authority: EPIC

# FHIR output
fhirMapping:
  targetVersion: R4
  bundleType: transaction
  resourceMappings: []

# Validation rules
validation:
  enabled: true
  requiredSegments: [MSH, PID, PV1]
  requiredFields: [MSH.9, PID.3]

Encoding Section

Controls Phase 1 (Byte Normalization).

encoding:
  charset: UTF-8 # UTF-8, ISO-8859-1, Windows-1252, US-ASCII
  lineEnding: auto # LF, CRLF, CR, auto
  bomHandling: strip # strip, preserve, error

Options

FieldValuesDescription
charsetUTF-8, ISO-8859-1, Windows-1252, US-ASCIICharacter encoding
lineEndingLF, CRLF, CR, autoLine ending style
bomHandlingstrip, preserve, errorBOM marker handling

Common Scenarios

Legacy system with Windows encoding:

encoding:
  charset: Windows-1252
  lineEnding: CRLF

Modern UTF-8 system:

encoding:
  charset: UTF-8
  lineEnding: auto
  bomHandling: strip

Syntax Section

Controls Phase 2 (Syntactic Parsing).

syntax:
  hl7Version: '2.5'
  fieldSeparator: '|'
  encodingChars: "^~\\&"
  escapeSequences:
    enabled: true
    customMappings:
      "\\N\\": '' # Null escape
      "\\.br\\": "\n" # Line break
  strictMode: false

Options

FieldDescription
hl7VersionExpected HL7 version (2.3, 2.3.1, 2.4, 2.5, 2.5.1, 2.6, 2.7, 2.8)
fieldSeparatorField delimiter (always | in practice)
encodingCharsComponent, repetition, escape, subcomponent chars
strictModeIf true, fail on any parse errors

Escape Sequences

Standard HL7 escapes:

  • \F\| (field separator)
  • \S\^ (component separator)
  • \T\& (subcomponent separator)
  • \R\~ (repetition separator)
  • \E\\ (escape character)
  • \H\ → highlight start
  • \N\ → normal text (highlight end)

Custom mappings override or extend these.

Semantics Section

Controls Phase 3 (Semantic Extraction).

semantics:
  messageTypes: [ADT, ORU]
  eventTypes: [A01, A02, A03, A04, A08, R01]

  patientIdentifiers:
    - source_field: PID.3.1
      identifier_type: MRN
      assigning_authority: EPIC
      validation: required
      format_hint: "\\d{6,8}"

    - source_field: PID.3.1
      identifier_type: SSN
      assigning_authority: SSA
      validation: optional

  encounterIdentifiers:
    - source_field: PV1.19
      identifier_type: VN
      assigning_authority: HOSPITAL

  customExtractors:
    - name: insurance_group
      source_field: IN1.8
      target: insurance.group_number

Identifier Configuration

patientIdentifiers:
  - source_field: PID.3.1 # HL7 field path
    identifier_type: MRN # Type code
    assigning_authority: EPIC # Authority name
    validation: required # required, optional, warn
    format_hint: "\\d{6,8}" # Regex for validation

Event Classification

Map HL7 trigger events to semantic events with patient class awareness:

eventClassification:
  adt_a01:
    default: patient_admit
    patient_class_values:
      I: inpatient_admit
      O: outpatient_admit
      E: emergency_admit
  adt_a03:
    default: patient_discharge

FHIR Mapping Section

Controls FHIR R4 output generation.

fhirMapping:
  targetVersion: R4 # R4 or R5
  bundleType: transaction # batch, transaction, collection

  resourceMappings:
    - event_type: patient_admit
      resources: [Patient, Encounter]
    - event_type: lab_result
      resources: [Patient, Observation, DiagnosticReport]

Bundle Types

TypeDescription
batchIndependent operations, partial success allowed
transactionAll-or-nothing, rollback on failure
collectionRead-only collection, no server processing

Validation Section

Controls message and field validation.

validation:
  enabled: true

  requiredSegments: [MSH, PID, PV1]
  requiredFields: [MSH.9, PID.3, PV1.2]

  customValidators:
    - name: mrn_format
      field: PID.3.1
      pattern: "^MRN\\d{6}$"
      message: "MRN must start with 'MRN' followed by 6 digits"

Validation Levels

  • enabled: true + requiredSegments → Fail if segments missing
  • enabled: true + requiredFields → Fail if fields empty
  • enabled: false → Warnings only, never fail

Tolerance Configuration

Configure what parsing issues to tolerate:

hl7v2:
  tolerate:
    missing_segments: [NK1, NTE, OBX]
    nte_anywhere: true
    extra_components: true
    unknown_segments: true

Options

OptionDescription
missing_segmentsList of segments that can be absent
nte_anywhereAllow NTE segments after any segment
extra_componentsIgnore extra components beyond expected
unknown_segmentsPass through unknown segments as raw

Z-Segment Configuration

Handle custom (vendor-specific) Z-segments:

hl7v2:
  z_segments:
    ZPD:
      description: 'Patient demographics extension'
      fields:
        - index: 1
          name: custom_mrn
          target: patient.identifiers.custom_mrn
        - index: 2
          name: payer_code
          target: insurance.payer_code

Terminology Mapping

Configure code system mappings:

terminology:
  race_mapping: local_to_omb # Map local race codes
  language_mapping: local_to_bcp47 # Map language codes

  custom_mappings:
    - source_system: LOCAL
      target_system: LOINC
      mapping_file: local_to_loinc.csv

Example Profiles

Minimal Profile

name: minimal
version: '1.0'

encoding:
  charset: UTF-8

syntax:
  hl7Version: '2.5'

Production ADT Profile

name: epic_adt_prod
version: '2.1'
description: 'Epic ADT interface - Production'

encoding:
  charset: UTF-8
  lineEnding: auto
  bomHandling: strip

syntax:
  hl7Version: '2.5.1'
  fieldSeparator: '|'
  encodingChars: "^~\\&"
  escapeSequences:
    enabled: true
  strictMode: false

hl7v2:
  tolerate:
    missing_segments: [NK1, NTE, AL1, DG1]
    extra_components: true
    unknown_segments: true
  z_segments:
    ZPD:
      fields:
        - index: 1
          name: epic_csn
          target: encounter.identifiers.epic_csn

semantics:
  messageTypes: [ADT]
  eventTypes: [A01, A02, A03, A04, A08, A11, A13, A40]
  patientIdentifiers:
    - source_field: PID.3.1
      identifier_type: MRN
      assigning_authority: EPIC
      validation: required

validation:
  enabled: true
  requiredSegments: [MSH, PID, PV1]
  requiredFields: [MSH.9, MSH.10, PID.3]

fhirMapping:
  targetVersion: R4
  bundleType: transaction

Lab Interface Profile

name: lab_interface
version: '1.0'
description: 'Lab results from reference lab'

encoding:
  charset: ISO-8859-1
  lineEnding: CRLF

syntax:
  hl7Version: '2.3'

semantics:
  messageTypes: [ORU]
  eventTypes: [R01]
  patientIdentifiers:
    - source_field: PID.3.1
      identifier_type: MRN

validation:
  enabled: true
  requiredSegments: [MSH, PID, OBR, OBX]

CLI Commands

Validate a Profile

fi-fhir validate profile my_profile.yaml

Infer Profile from Samples

fi-fhir profile infer samples/*.hl7 --output inferred_profile.yaml

Lint Profile for Best Practices

fi-fhir profile lint my_profile.yaml

Parse with Profile

fi-fhir parse --format hl7v2 --profile my_profile.yaml message.hl7

See Also