FlexpaFlexpa
Developer PortalGet SandboxTry it yourself

All docs

Overview

    How it works

    • Deterministic IDs

    Encounter generation

    • Class determination
    • Encounter type
    • Period
    • Status mapping
    • Participants
    • Hospitalization
    • Location and service provider

    Condition generation

    • Diagnosis source
    • Category and status
    • Deduplication
    • Deterministic IDs
    • Encounter linkage
    • Recorded date

    Procedure generation

    • Code sources
    • Deduplication
    • Code normalization
    • Deterministic IDs
    • Encounter linkage
    • Performed date
    • Payer data quality

    Provenance

      Identifying generated resources

      • Claims-derived extension
      • Source EOB identifier

      Next steps

        Claims to clinical

        #Overview

        Health insurers expose claims data through Patient Access APIs as FHIR ExplanationOfBenefit (EOB) resources. While EOBs are rich in clinical signal — diagnoses, procedures, providers, service dates — they are financial artifacts, not clinical records. Most FHIR-native tooling expects clinical resource types like Encounter, Condition, and Procedure.

        Flexpa bridges this gap with resource generation: a deterministic pipeline that derives US Core-conformant clinical resources from EOB claims data. Resource generation runs on payer endpoints Flexpa has identified as missing clinical resources. When active, generated resources are appended to the FHIR Bundle alongside the original EOBs, giving your application a unified clinical view without losing the underlying claims detail.

        • Encounter — one per eligible EOB unless a matching Encounter already exists, capturing the clinical visit context (class, period, participants, location)
        • Condition — one per diagnosis on the EOB, with ICD-10/SNOMED coding
        • Procedure — one per procedure on the EOB, with CPT/HCPCS coding
        • Provenance — one per EOB, linking all generated resources back to their source claim

        Every generated resource is marked with a claims-derived extension so your application can distinguish generated clinical resources from those returned natively by the source.

        This resource generation engine is also available as a standalone transformer for payers implementing the Da Vinci PDex mappings on ExplanationOfBenefit data. If you are a payer interested in using this engine to produce US Core clinical resources from your claims data, contact us.

        Resource generation pipeline: EOBs from the Payer API flow through the transform pipeline where resource generation produces Encounter, Condition, Procedure, and Provenance resources appended to the augmented FHIR Bundle.

        #How it works

        Resource generation runs as part of Flexpa's sync transform pipeline — after coding normalization and async preparation, but before enrichment steps like tagging and batching. The pipeline is gated by the expandClaimsToClinical endpoint option; when disabled, EOBs pass through unmodified.

        For each ExplanationOfBenefit in the bundle:

        • Filter: cancelled, draft, and entered-in-error EOBs are skipped; pharmacy EOBs are filtered during Encounter generation — when no Encounter is produced, any Conditions and Procedures generated from that EOB are discarded
        • Generate Conditions: one Condition per diagnosis coding on the EOB
        • Generate Procedures: one Procedure per procedure coding on the EOB
        • Generate Encounter: one Encounter per EOB unless a matching Encounter already exists, with reasonReference linking to the generated Conditions
        • Generate Provenance: one Provenance linking all generated resources back to the source EOB

        #Deterministic IDs

        Every generated resource receives a deterministic ID computed from the source EOB ID, resource type, and patient namespace. The same EOB always produces the same resource IDs, making the pipeline idempotent across re-syncs.

        ID = UUIDv5(destinationPatientId, resourceType + ":" + eobId + ":" + key)

        Provenance links generated Encounter, Condition, and Procedure resources back to the source ExplanationOfBenefit via entity.what references.

        Claims-derived extension

        {
          "resourceType": "Encounter",
          "id": "a1b2c3d4...",
          "meta": {
            "profile": [
              "http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter"
            ]
          },
          "extension": [
            {
              "url": "https://api.flexpa.com/fhir/StructureDefinition/claims-derived",
              "valueBoolean": true
            }
          ]
        }
        

        #Encounter generation

        Each non-pharmacy EOB produces exactly one Encounter conforming to the US Core 6.1 Encounter profile. The Encounter captures the clinical context of the claim: what kind of visit it was, when it happened, who was involved, and why. The Da Vinci PDex Encounter mapping serves as a loose starting point for field derivation, with Flexpa-specific extensions for payer data patterns not covered by the guide.

        #Class determination

        The Encounter class (ambulatory, inpatient, emergency, etc.) is derived from multiple signals on the EOB, evaluated in priority order:

        • Emergency — POS code 23 or NUBC revenue codes 0450-0459
        • Blue Button eob-type — HHA → home health, INPATIENT/SNF → inpatient, DME/OUTPATIENT → ambulatory
        • Inpatient-facility — CARIN BB inpatient-facility type coding
        • Discharge + inpatient signal — discharge disposition present with corroborating type-of-bill (11x/12x) or admission period
        • Telehealth — POS codes 02/10 or telephone/online CPT codes (99441-99443, 99421-99423, 98966-98968, 99457-99458). POS-based telehealth detection is suppressed when all items are hands-on physical therapy CPT codes, which some payers misfile under POS 10
        • Home health — POS code 12, HCPCS home health codes (T1019-T1022, T1030-T1031, S5170-S5171, 99509), CPT home visit E/M codes (99341-99350), or home health revenue codes (055x-058x)
        • Inpatient POS/CPT — POS codes 21 (inpatient hospital), 31/32 (nursing facility), 85 (SNF/custodial), or nursing facility CPT codes (99304-99318) signal inpatient care
        • Institutional — institutional claim type with inpatient indicators → IMP, otherwise → AMB
        • Ambulance — POS 41/42 or HCPCS A01xx/A02xx/A04xx → FLD (field encounter)
        • Default — professional, oral, and vision claims default to AMB (ambulatory), unless billed in an inpatient setting (e.g., POS 21) in which case they map to IMP

        #Encounter type

        Each Encounter includes a SNOMED encounter type coding that provides more clinical specificity than the class alone. The pipeline distinguishes between telemedicine (POS 02/10, synchronous audio-video) and telephone (CPT codes only, audio-only) encounter types. When a CPT code in the Evaluation & Management (99201-99499), home health (99500-99600), medication management (99605-99607), or non-physician telephone (98966-98968) range is present on an EOB item, it is included as a secondary type coding.

        #Period

        The encounter period is derived with fallback logic:

        • Inpatient claims: admission period from CARIN BB admissionperiod supportingInfo takes precedence, with billable period filling gaps
        • Outpatient claims: billablePeriod from the EOB, filtered for sentinel dates (1900-01-01, 2078-12-31, 9999-12-31)
        • Fallback: min/max of item-level servicedDate and servicedPeriod across all line items
        • Missing: Data Absent Reason extension when no valid dates are available

        Billable periods spanning 365+ days are treated as coverage years — the end date is dropped, keeping only the start (service date).

        Encounter generation flow: EOB is checked for pharmacy type, then class is determined from POS codes, revenue codes, type-of-bill, and CPT codes. The resulting Encounter includes status, class, type, period, subject, participants, reasonReference, hospitalization, location, and claims-derived extension.

        #Status mapping

        EOB status maps to Encounter status per Da Vinci PDex:

        EOB statusEncounter status
        activefinished
        cancelledcancelled
        draftin-progress
        entered-in-errorentered-in-error

        #Participants

        Care team members from the EOB are mapped to Encounter participants with role translation:

        EOB careTeam roleEncounter participant type
        primary / attendingATND (attender)
        performing / operatingPPRF (primary performer)
        referringREF (referrer)
        supervisingSPRF (secondary performer)

        Participants are deduplicated by provider reference and role code.

        #Hospitalization

        Discharge disposition is included only for inpatient (IMP) encounters, extracted from EOB supportingInfo with category discharge-status. Outpatient claims that carry discharge disposition data are excluded to avoid misleading hospitalization records.

        #Location and service provider

        The Encounter includes location from the EOB's facility field and serviceProvider from the EOB's provider field when it references an Organization. Fragment-referenced contained resources from the EOB are carried over to the Encounter.

        Generated Encounter example

        {
          "resourceType": "Encounter",
          "id": "a1b2c3d4e5f6...",
          "meta": {
            "profile": [
              "http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter"
            ]
          },
          "extension": [{
            "url": "https://api.flexpa.com/fhir/StructureDefinition/claims-derived",
            "valueBoolean": true
          }],
          "identifier": [{
            "system": "https://fhir.flexpa.com/identifiers/SourceEOBId",
            "value": "eob-12345"
          }],
          "status": "finished",
          "class": {
            "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
            "code": "AMB",
            "display": "ambulatory"
          },
          "type": [{
            "coding": [
              {
                "system": "http://snomed.info/sct",
                "code": "308335008",
                "display": "Patient encounter procedure"
              },
              {
                "system": "http://www.ama-assn.org/go/cpt",
                "code": "99213"
              }
            ]
          }],
          "subject": {
            "reference": "Patient/p-abc123"
          },
          "participant": [{
            "type": [{
              "coding": [{
                "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
                "code": "ATND",
                "display": "attender"
              }]
            }],
            "individual": {
              "reference": "Practitioner/dr-smith"
            }
          }],
          "period": {
            "start": "2025-03-15",
            "end": "2025-03-15"
          },
          "reasonReference": [
            { "reference": "Condition/cond-1" },
            { "reference": "Condition/cond-2" }
          ]
        }
        

        #Condition generation

        Each diagnosis on a non-pharmacy EOB produces a Condition resource conforming to the US Core 6.1 Condition Encounter Diagnosis profile. The Da Vinci PDex Condition mapping serves as a loose starting point.

        #Diagnosis source

        Conditions are extracted from eob.diagnosis[] entries that include a diagnosisCodeableConcept with at least one coded value. The following entries are skipped:

        • Reference-only — entries with only a diagnosisReference (a pointer to an external Condition that should already exist in the bundle)
        • Text-only — entries where the coding array is absent or empty, with no structured code to carry forward
        • Missing identity — entries with codings that lack both a system and code value, use the NullFlavor code system, or have single-character codes

        #Category and status

        All generated Conditions are categorized as encounter-diagnosis — diagnoses recorded in the context of a specific claim/visit rather than problem-list items maintained over time.

        FieldValueRationale
        clinicalStatusactiveClaims capture point-in-time diagnoses without lifecycle tracking
        verificationStatusconfirmedAdjudicated claims represent confirmed diagnoses
        categoryencounter-diagnosisTied to a specific visit, not a longitudinal problem list

        #Deduplication

        Diagnoses are deduplicated by code system + code value within each EOB. If a payer lists the same ICD-10 code multiple times (e.g., as both a principal and secondary diagnosis), only one Condition is generated.

        #Deterministic IDs

        Condition IDs are deterministic UUIDv5 hashes derived from the EOB ID and the payer-assigned diagnosis sequence number (with array index fallback). The same EOB always produces the same Condition IDs, making the pipeline idempotent across re-syncs.

        ID = UUIDv5(destinationPatientId, eobId + ":condition:" + sequence)

        #Encounter linkage

        Every generated Condition includes an encounter reference to the Encounter generated from the same EOB. The Encounter in turn includes reasonReference entries pointing back to these Conditions, creating a bidirectional link between visit and diagnoses.

        #Recorded date

        The recordedDate represents when the diagnosis was documented, resolved with fallback logic and sentinel date filtering:

        • billablePeriod.start → first item.servicedDate or item.servicedPeriod.start → billablePeriod.end → eob.created
        • Sentinel dates (1900-01-01, 2078-12-31, 9999-12-31) are skipped at each candidate
        • Missing: Data Absent Reason extension when no valid date is available
        Condition generation flow: diagnoses are extracted from eob.diagnosis[], filtered to entries with diagnosisCodeableConcept, deduplicated by code system and value, categorized as encounter-diagnosis with active clinical status and confirmed verification status, and dated via recorded date fallback chain.

        Generated Condition example

        {
          "resourceType": "Condition",
          "id": "c1d2e3f4a5b6...",
          "meta": {
            "profile": [
              "http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition-encounter-diagnosis"
            ]
          },
          "extension": [{
            "url": "https://api.flexpa.com/fhir/StructureDefinition/claims-derived",
            "valueBoolean": true
          }],
          "clinicalStatus": {
            "coding": [{
              "system": "http://terminology.hl7.org/CodeSystem/condition-clinical",
              "code": "active",
              "display": "Active"
            }]
          },
          "verificationStatus": {
            "coding": [{
              "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status",
              "code": "confirmed",
              "display": "Confirmed"
            }]
          },
          "category": [{
            "coding": [{
              "system": "http://terminology.hl7.org/CodeSystem/condition-category",
              "code": "encounter-diagnosis",
              "display": "Encounter Diagnosis"
            }]
          }],
          "code": {
            "coding": [{
              "system": "http://hl7.org/fhir/sid/icd-10-cm",
              "code": "M54.5",
              "display": "Low back pain"
            }]
          },
          "subject": {
            "reference": "Patient/p-abc123"
          },
          "encounter": {
            "reference": "Encounter/a1b2c3d4e5f6..."
          },
          "recordedDate": "2025-03-15"
        }
        

        #Procedure generation

        Each non-pharmacy EOB can produce one or more Procedure resources conforming to the US Core 6.1 Procedure profile. The Da Vinci PDex Procedure mapping serves as a loose starting point, with additional logic for item-level extraction and procedural code classification.

        #Code sources

        Procedure codes are extracted from two locations on each EOB:

        • eob.procedure[] — explicit procedures the payer coded on the claim, trusted as-is
        • eob.item[].productOrService — line-item codes filtered to procedural services only (surgeries, imaging, therapeutic interventions, diagnostics)

        Non-procedural services — E/M visits, lab tests, supplies, drugs, and transport — are excluded from item-level extraction. Recognized procedural code systems include CPT, HCPCS, ICD-10-PCS, and CDT.

        #Deduplication

        Procedures are deduplicated differently depending on their source.

        Explicit procedures from eob.procedure[] (Phase 1) are not deduplicated — each entry is trusted as a distinct procedure.

        Item-level procedures (Phase 2) are deduplicated by base code + service date, so the same code on different dates produces distinct Procedure resources (separate sessions), while the same code on the same date is collapsed.

        Cross-phase deduplication skips item-level codes that were already extracted from eob.procedure[].

        Several signals override this default collapsing:

        • Distinct procedure modifiers — CMS modifiers like 59 (distinct procedural service), 50 (bilateral), 76/77 (repeat procedure) indicate genuinely separate procedures even with the same code and date
        • Body site — the same code on the same date but on different body sites (e.g., dental procedures on different teeth) produces distinct Procedures
        • Oral/dental claims — each line item on an oral claim type is treated as a separate procedure, since dental claims typically represent one procedure per tooth/site

        #Code normalization

        Some payers concatenate modifiers onto CPT codes (e.g., 72100TC, 7210026). The pipeline extracts the base 5-digit code before classification and deduplication. Category III CPT codes (4 digits + T, e.g., 0296T) are also recognized as procedural. Contradictory data-absent-reason extensions are stripped when a valid code exists.

        #Deterministic IDs

        Procedure IDs are deterministic UUIDv5 hashes derived from the EOB ID and a stable key — the payer-assigned sequence number for explicit procedures, or the base code + date for item-level procedures. This ensures idempotent output across re-syncs even if array order changes.

        ID = UUIDv5(destinationPatientId, eobId + ":procedure:" + key)

        #Encounter linkage

        Every generated Procedure includes an encounter reference pointing to the Encounter generated from the same EOB. Generated Conditions are also appended to the Encounter's reasonReference, creating a bidirectional link between the visit and its clinical context.

        #Performed date

        The procedure date is resolved with fallback logic and sentinel date filtering:

        • Explicit procedures: procedure.date → billablePeriod.start
        • Item-level: item.servicedDate → item.servicedPeriod.start → billablePeriod.start
        • Sentinel dates (1900-01-01, 2078-12-31, 9999-12-31) are skipped at each candidate
        • Missing: Data Absent Reason extension when status is completed (US Core us-core-7 invariant)

        #Payer data quality

        Payers frequently miscategorize CDT (dental) codes under CPT or HCPCS system URIs. The pipeline detects CDT codes by pattern (e.g., D2740) regardless of the declared code system, ensuring dental procedures are correctly identified and generated.

        Procedure generation flow: procedure codes are extracted from eob.procedure[] and eob.item[].productOrService, filtered to procedural CPT/HCPCS/ICD-10-PCS/CDT codes, deduplicated, normalized, and resolved with date fallback chains.

        Generated Procedure example

        {
          "resourceType": "Procedure",
          "id": "f8e7d6c5b4a3...",
          "meta": {
            "profile": [
              "http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure"
            ]
          },
          "extension": [{
            "url": "https://api.flexpa.com/fhir/StructureDefinition/claims-derived",
            "valueBoolean": true
          }],
          "status": "completed",
          "code": {
            "coding": [{
              "system": "http://www.ama-assn.org/go/cpt",
              "code": "27447",
              "display": "Total knee replacement"
            }]
          },
          "subject": {
            "reference": "Patient/p-abc123"
          },
          "encounter": {
            "reference": "Encounter/a1b2c3d4e5f6..."
          },
          "performedDateTime": "2025-03-15"
        }
        

        Item-level Procedure (imaging)

        {
          "resourceType": "Procedure",
          "id": "b2c3d4e5f6a7...",
          "meta": {
            "profile": [
              "http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure"
            ]
          },
          "extension": [{
            "url": "https://api.flexpa.com/fhir/StructureDefinition/claims-derived",
            "valueBoolean": true
          }],
          "status": "completed",
          "code": {
            "coding": [{
              "system": "http://www.ama-assn.org/go/cpt",
              "code": "72100"
            }]
          },
          "subject": {
            "reference": "Patient/p-abc123"
          },
          "encounter": {
            "reference": "Encounter/a1b2c3d4e5f6..."
          },
          "performedDateTime": "2025-06-01"
        }
        

        #Provenance

        Every EOB that produces generated resources also produces a Provenance resource that establishes a traceable link between the generated clinical resources and their source claim.

        • target — references all generated resources (Encounter, Conditions, Procedures) from this EOB
        • entity.what — references the source EOB, with role source
        • activity — Derivation from the W3C Provenance Activity Type system
        • agent — identifies the Flexpa Transform Pipeline as the assembler
        • recorded — timestamp of generation

        This provenance chain lets your application answer: "Where did this Encounter come from?" — tracing back to the specific claim that produced it.

        Generated Provenance example

        {
          "resourceType": "Provenance",
          "id": "prov-a1b2c3...",
          "target": [
            { "reference": "Encounter/enc-1" },
            { "reference": "Condition/cond-1" },
            { "reference": "Condition/cond-2" },
            { "reference": "Procedure/proc-1" }
          ],
          "recorded": "2025-03-15T12:00:00Z",
          "activity": {
            "coding": [{
              "system": "http://hl7.org/fhir/w3c-provenance-activity-type",
              "code": "Derivation",
              "display": "Derivation"
            }]
          },
          "agent": [{
            "type": {
              "coding": [{
                "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
                "code": "assembler",
                "display": "Assembler"
              }]
            },
            "who": {
              "display": "Flexpa Transform Pipeline"
            }
          }],
          "entity": [{
            "role": "source",
            "what": {
              "reference": "ExplanationOfBenefit/eob-12345"
            }
          }]
        }
        

        #Identifying generated resources

        All generated resources share two markers that let your application identify and handle them appropriately:

        #Claims-derived extension

        Every generated resource includes the extension:

        https://api.flexpa.com/fhir/StructureDefinition/claims-derived
        

        with valueBoolean: true. Query for this extension to filter or flag claims-derived resources in your application.

        #Source EOB identifier

        Each generated Encounter carries an identifier with system https://fhir.flexpa.com/identifiers/SourceEOBId containing the ID of the source EOB. This provides a direct lookup path from any Encounter back to the claim that generated it.

        Generated resources conform to US Core profiles and pass through the same validation, tagging, and enrichment pipeline as natively-sourced resources. They are indistinguishable in structure from provider-sourced resources — only the claims-derived extension marks them as generated.

        Filtering claims-derived resources

        // Check if a resource was generated from claims
        function isClaimsDerived(resource) {
          return resource.extension?.some(
            ext =>
              ext.url === 'https://api.flexpa.com/fhir/StructureDefinition/claims-derived' &&
              ext.valueBoolean === true
          );
        }
        
        // Find the source EOB for an Encounter
        function getSourceEobId(encounter) {
          return encounter.identifier?.find(
            id => id.system === 'https://fhir.flexpa.com/identifiers/SourceEOBId'
          )?.value;
        }
        

        #Next steps

        FHIR Records

        Complete reference for retrieving and working with FHIR resources through Flexpa's API

        View Records Documentation →

        Flexpa API

        Use Flexpa API as a unified API to access Explanation of Benefits and more

        Build with Flexpa API →
        Status TwitterGitHub

        © 2026 Flexpa. All rights reserved.