Nirmitee.io
Healthcare

EDI 837 and 835: The Developer's Guide to Healthcare Claims Integration

April 26, 202618 min readUpdated Sep 26, 2026
Written by
Dinesh Thorat
Dinesh Thorat

Digital Growth Lead

Writes about healthcare technology, interoperability, and AI-driven transformation across modern care systems.

EDI 837 and 835: The Developer's Guide to Healthcare Claims Integration

If you are building healthcare claims software and have never parsed an X12 835 file at 2 AM trying to figure out why adjustment reason code CO-45 posted to the wrong bucket, this guide is for you. And if you have done that, this guide is still for you, because the entire EDI ecosystem is poorly documented, scattered across expensive implementation guides, and full of vendor-specific quirks that no specification covers.

Healthcare Electronic Data Interchange (EDI) is the backbone of every claims transaction in the United States. Over $4 trillion in healthcare spending flows through EDI transactions annually. HIPAA mandated these electronic formats in 1996, and the ASC X12 standard defines every segment, element, and code value. Yet finding a developer-focused guide that explains how these transactions actually work in production, with real segment structures, parsing code, and integration patterns, is nearly impossible.

This guide covers the six core EDI transaction sets you will encounter when building healthcare payer integrations: 837 (claims), 835 (remittance), 270/271 (eligibility), 276/277 (claim status), and 278 (prior authorization). We include actual segment structures, Python parsing examples, FHIR mapping patterns, and the clearinghouse integration architecture that connects it all.

Building 835 and 837 integrations into your product? Our team works inside a live Waystar X12 claims pipeline (837P, 835, 270/271, 276/277 and 999). We are ISO 27001:2022 certified and sign BAAs. See our X12 EDI integration services, or send us your EDI integration scope.

What Is EDI in Healthcare?

Electronic Data Interchange (EDI) in healthcare is a standardized format for exchanging administrative and financial data between providers, payers, clearinghouses, and other covered entities. The standard is maintained by the Accredited Standards Committee X12 (ASC X12), and HIPAA mandates its use for specific transaction types.

The X12 Standard

ASC X12 defines the syntax, structure, and semantics for healthcare electronic transactions. The current version used in US healthcare is 005010 (sometimes written as 5010), which replaced the older 004010 version in 2012. Every X12 transaction follows a hierarchical envelope structure:

  • ISA/IEA: Interchange envelope (outermost wrapper, identifies sender/receiver)
  • GS/GE: Functional group (groups related transactions by type)
  • ST/SE: Transaction set (individual transaction like a single claim or remittance)

Each segment within the transaction consists of an identifier (like CLM or SV1) followed by data elements separated by a delimiter (typically *). Segments are terminated by a segment terminator (typically ~). This delimiter-based format makes EDI files compact but notoriously difficult to read without a parser.

HIPAA-Mandated Transactions

HIPAA's Administrative Simplification provisions mandate electronic formats for these transaction types:

Transaction SetX12 CodePurposeDirection
Health Care Claim837P / 837I / 837DSubmit claims for paymentProvider to Payer
Health Care Claim Payment/Advice835Explain payments and adjustmentsPayer to Provider
Eligibility Inquiry270Check patient insurance coverageProvider to Payer
Eligibility Response271Return coverage/benefit informationPayer to Provider
Claim Status Inquiry276Check status of submitted claimProvider to Payer
Claim Status Response277Return claim processing statusPayer to Provider
Authorization Request278Request/respond to prior authBidirectional
Enrollment/Disenrollment834Communicate enrollment changesSponsor to Payer
Payment/Remittance820Premium paymentSponsor to Payer

Every covered entity (healthcare provider, health plan, or clearinghouse) must be able to send and receive these transactions in the standard X12 format. Non-compliance carries penalties under HIPAA enforcement.

835 vs 837: Key Differences

An 837 is the claim a provider sends to a payer to ask for payment. An 835 is the payer's answer: the electronic remittance advice (ERA) that says what was paid, what was adjusted and why, and what the patient owes. The 837 travels from provider to payer before adjudication, and the 835 travels back from payer to provider after it, usually on the same cycle as the payment itself.

 837 (the claim)835 (the remittance)
X12 nameHealth Care ClaimHealth Care Claim Payment/Advice (electronic remittance advice, ERA)
Sent byThe provider or its billing service, usually through a clearinghouseThe payer, often through a clearinghouse
Received byThe payerThe provider or its billing service
When it is sentAfter the visit is coded and billedAfter the payer adjudicates the claim, on the payer's payment cycle
5010 implementation guide005010X222A1 (837P), 005010X223A2 (837I), 005010X224A2 (837D)005010X221A1
Core segmentsCLM (claim), HI (diagnoses), SV1 / SV2 / SV3 (service lines)BPR (payment), TRN (trace number), CLP (claim payment), SVC (service line), CAS (adjustments), PLB (provider-level adjustments)
Money it carriesBilled charges (CLM02)Paid amount (CLP04), patient responsibility (CLP05) and every adjustment by group and reason code
What comes backA 999 for file syntax, a 277CA saying whether each claim was accepted into adjudication, then the 835Nothing is required. The provider matches the BPR amount and TRN trace number to the EFT deposit or check
Paper equivalentCMS-1500 (professional), UB-04 (institutional), ADA dental claim formThe paper remittance advice mailed with a check
FHIR equivalentClaimExplanationOfBenefit

How an 835 Matches Back to Its 837

An 835 points back to its 837 through the identifiers the provider sent, not through a shared file ID. CLP01 in the 835 returns the patient control number from CLM01 in the 837, and REF*6R in the 835 service line loop (2110) returns the line item control number sent in REF*6R in the 837 service line loop (2400). CLP07 adds the payer's own claim control number, which a corrected claim or an appeal has to quote.

837, loop 2300:  CLM*PATIENT_ACCT_001*250***11:B:1*Y*A*Y*Y~
835, loop 2100:  CLP*PATIENT_ACCT_001*1*250.00*125.00**12*PAYER_CLM_001*11~

The two files are not one to one. A single 835 usually covers many claims from many 837 batches, grouped by payment. A single claim can appear in more than one 835: a reversal (CLP02 = 22) followed by a corrected payment, or a primary payer's 835 followed later by the secondary payer's. Match on CLP01 plus the line-level REF*6R, and never assume the 835 arrives in the same order or grouping as the 837s you sent. To see the pairing on your own files, open an 837 and its 835 in the free X12 EDI Inspector, which parses both in the browser.

EDI 837: Healthcare Claims

The 837 transaction set is the electronic equivalent of the CMS-1500 (professional) or UB-04 (institutional) paper claim form. It contains everything a payer needs to adjudicate a claim: patient demographics, provider information, diagnosis codes, procedure codes, charges, and supporting documentation references.

837P: Professional Claims

The 837P (Professional) transaction is defined in implementation guide 005010X222A1. It is used for claims from physicians, outpatient clinics, ambulatory surgery centers, and other non-institutional providers. The key segments are:

ISA*00*          *00*          *ZZ*SENDER_ID      *ZZ*RECEIVER_ID    *260319*1430*^*00501*000000001*0*P*:~
GS*HC*SENDER_CODE*RECEIVER_CODE*20260319*1430*1*X*005010X222A1~
ST*837*0001*005010X222A1~
BHT*0019*00*CLAIM_REF_001*20260319*1430*CH~
NM1*85*1*SMITH*JOHN****XX*1234567890~
N3*123 MEDICAL DRIVE~
N4*CHICAGO*IL*60601~
REF*EI*123456789~
NM1*87*2*BILLING GROUP INC****XX*9876543210~
HL*1**20*1~
NM1*IL*1*DOE*JANE****MI*XYZ123456789~
N3*456 PATIENT STREET~
N4*CHICAGO*IL*60602~
DMG*D8*19850115*F~
SBR*P*18*GROUP_PLAN_001******CI~
CLM*PATIENT_ACCT_001*250***11:B:1*Y*A*Y*Y~
HI*ABK:J06.9~
DTP*472*D8*20260315~
LX*1~
SV1*HC:99213*125*UN*1***1~
DTP*472*D8*20260315~
LX*2~
SV1*HC:87880*85*UN*1***1~
DTP*472*D8*20260315~
REF*6R*LINE_REF_002~
SE*25*0001~
GE*1*1~
IEA*1*000000001~

The critical segments to understand:

  • CLM (Claim Information), contains the patient account number, total charge amount, place of service code (11 = office, 21 = inpatient hospital, 22 = outpatient hospital), and claim filing indicator
  • SV1 (Professional Service Line), Each line item with CPT/HCPCS code, charge amount, unit count, and modifier. The format is SV1*HC:CPT_CODE:MODIFIER*CHARGE*UN*UNITS***DIAGNOSIS_POINTER~
  • HI (Health Care Diagnosis Code), ICD-10 diagnosis codes. ABK qualifier = principal diagnosis, ABF = other diagnosis
  • NM1*85 (Billing Provider), NPI and name of the billing entity
  • NM1*IL (Subscriber), Member/subscriber identification

Annotated 837P Claim With Three Errors

Most 837 rejections come from a handful of data errors, not from exotic loops. The synthetic excerpt below (claim, rendering provider and service lines; no real patient or provider) contains three errors that a clearinghouse or payer would reject.

CLM*SYNTH1001*150***11:B:1*Y*A*Y*Y~
HI*ABK:J069~
NM1*82*1*SAMPLE*JANE****XX*123456789~
LX*1~
SV1*HC:99213*100*UN*1***1~
DTP*472*D8*20260931~
LX*2~
SV1*HC:87880*40*UN*1***1~
DTP*472*D8*20260915~
LineErrorWhy it is rejectedFix
CLM02 = 150Claim total does not equal the line charges (100 + 40 = 140)The total claim charge must equal the sum of the service line charges; clearinghouse edits reject the claimSet CLM02 to 140, or correct the line charges
NM109 = 123456789Rendering provider NPI has 9 digitsAn NPI is 10 digits with a check digit, so the identifier fails validationSend the provider's 10-digit NPI and confirm it in NPPES
DTP03 = 20260931Service date 31 September does not existInvalid dates fail syntax validation before adjudicationSend the real date of service in CCYYMMDD

Paste a file into our free EDI Inspector to decode each segment in the browser, and see our EDI testing guide for catching these errors before a clearinghouse does.

837I: Institutional Claims

The 837I (Institutional) transaction is defined in implementation guide 005010X223A2. Used by hospitals, skilled nursing facilities, home health agencies, and other institutional providers. The critical difference from 837P:

  • SV2 replaces SV1; uses revenue codes instead of CPT codes as the primary identifier. Format: SV2*REVENUE_CODE*HC:HCPCS_CODE*RATE*UN*UNITS~
  • CL1 (Institutional Claim Code), contains admission type, admission source, and patient status codes unique to facility claims
  • Type of Bill (TOB): A 3-digit code embedded in the CLM segment indicating facility type (hospital, SNF, HHA), bill classification (inpatient, outpatient), and frequency (admit, interim, final)

837D: Dental Claims

The 837D follows a similar structure but uses dental-specific procedure codes (CDT codes) and has segments for tooth number, oral cavity designation, and dental-specific qualifying information. Defined in 005010X224A2.

EDI 835: Remittance Advice (ERA)

Plain-English walkthrough: for a full 835 decoded line by line, see What Is an 835 File? ERA Explained With a Real Example.

The 835 transaction set is the Electronic Remittance Advice (ERA). It tells you exactly what the payer paid, what they adjusted, why they adjusted it, and what the patient owes. This is the transaction that drives payment posting automation in every practice management and billing system.

835 Segment Structure

The 835 is defined in implementation guide 005010X221A1. Here is a simplified example:

ISA*00*          *00*          *ZZ*PAYER_ID       *ZZ*PROVIDER_ID    *260319*0800*^*00501*000000001*0*P*:~
GS*HP*PAYER_CODE*PROVIDER_CODE*20260319*0800*1*X*005010X221A1~
ST*835*0001~
BPR*I*1250.00*C*ACH*CCP*01*999999999*DA*123456789*1234567890**01*999888777*DA*987654321*20260319~
TRN*1*TRACE_NUMBER_001*1234567890~
DTM*405*20260319~
N1*PR*BLUE CROSS BLUE SHIELD~
N1*PE*DR SMITH MEDICAL GROUP*XX*1234567890~
CLP*PATIENT_ACCT_001*1*250.00*125.00**12*PAYER_CLM_001*11~
NM1*QC*1*DOE*JANE~
SVC*HC:99213*125.00*100.00**1~
CAS*CO*45*25.00~
CAS*PR*2*25.00~
DTM*472*20260315~
SVC*HC:87880*85.00*25.00**1~
CAS*CO*45*35.00~
CAS*PR*1*25.00~
DTM*472*20260315~
CLP*PATIENT_ACCT_002*4*500.00*0.00**12*PAYER_CLM_002*21~
CAS*CO*4*500.00~
SE*20*0001~
GE*1*1~
IEA*1*000000001~

The critical segments for auto-posting:

  • BPR (Beginning Segment for Payment), Contains payment type (I=remittance info), total payment amount, payment method (ACH, check), and banking information for EFT reconciliation
  • TRN (Trace Number), The check number or EFT trace number used to match the ERA to a bank deposit
  • CLP (Claim Level), Maps to an individual claim. Contains the patient account number (for matching to your internal claim), claim status (1=processed, 2=denied, 4=denied, 22=reversed), charged amount, paid amount, and payer claim control number
  • SVC (Service Line), Individual procedure line within a claim. Shows what was billed, what was paid, and the units
  • CAS (Claim Adjustment Segment), The adjustment details. Each CAS segment contains an adjustment group code and one or more reason code/amount pairs

CAS Adjustment Group Codes

Understanding CAS group codes is essential for correct payment posting:

GroupCodeMeaningPosting Action
COContractual ObligationPayer's contractual write-offWrite off, do NOT bill patient
PRPatient ResponsibilityPatient owes this amountTransfer to patient balance
OAOther AdjustmentNeither contractual nor patientReview individually
PIPayer InitiatedPayer-initiated reductionReview, may warrant appeal
CRCorrection/ReversalCorrecting a prior paymentReverse and repost

Auto-Posting Logic

Here is the decision logic most billing systems implement for ERA auto-posting:

  1. Match CLP to internal claim: Use the patient account number (CLP01) or payer claim number (CLP07) to find the matching claim in your system
  2. Check claim status: CLP02 value: 1/2 = primary payer processed, 19 = processed primary forwarded to secondary, 22 = reversal, 4 = denied
  3. Process each SVC line: Match procedure code to the claim's line items
  4. Apply CAS adjustments: CO amounts = contractual write-off, PR amounts = patient responsibility, OA/PI = flag for review
  5. Post insurance payment: The SVC paid amount goes to the insurance payment bucket
  6. Transfer patient responsibility: Sum of PR adjustment amounts moves to patient balance
  7. Handle denials: CLP02 = 4 or CAS with denial reason codes (CO-4, CO-16, CO-97) route to denial work queue
  8. Reconcile to deposit: Match BPR payment amount + TRN trace to bank deposit

EDI 270/271: Eligibility Verification

The 270 (Eligibility Inquiry) and 271 (Eligibility Response) transaction pair is used to verify a patient's insurance coverage before rendering services. This is the most frequently executed EDI transaction; most practices run eligibility checks for every scheduled appointment.

270 Request Structure

ISA*00*          *00*          *ZZ*PROVIDER_ID    *ZZ*PAYER_ID       *260319*1000*^*00501*000000001*0*P*:~
GS*HS*PROVIDER_CODE*PAYER_CODE*20260319*1000*1*X*005010X279A1~
ST*270*0001*005010X279A1~
BHT*0022*13*ELIG_REQ_001*20260319*1000~
HL*1**20*1~
NM1*PR*2*BLUE CROSS BLUE SHIELD****PI*12345~
HL*2*1*21*1~
NM1*1P*2*DR SMITH MEDICAL GROUP****XX*1234567890~
HL*3*2*22*0~
NM1*IL*1*DOE*JANE****MI*XYZ123456789~
DMG*D8*19850115*F~
DTP*291*D8*20260319~
EQ*30~
SE*13*0001~
GE*1*1~
IEA*1*000000001~

Key elements: NM1*IL identifies the subscriber with member ID, DTP*291 specifies the date of service for eligibility, and EQ*30 requests health benefit plan coverage information (code 30 = all plan information).

271 Response Structure

The 271 response returns detailed benefit information through EB (Eligibility/Benefit) segments:

EB*1*IND*30*CI*PREMIER PLAN~
EB*C*IND*30**25.00~
EB*G*IND*30**500.00****23*20260101-20261231~
EB*G*IND*30**350.00***Y*23*20260101-20261231~

EB segment element meanings:

  • EB01: Eligibility code: 1=Active Coverage, 6=Inactive, 8=Not Covered, C=Co-Payment, G=Deductible
  • EB02: Coverage level: IND=Individual, FAM=Family, EMP=Employee, CHD=Child
  • EB03: Service type code: 30=Health Benefit Plan, 98=Professional Physician Visit, AL=Vision
  • EB05: Amount (co-pay amount, deductible amount, etc.)
  • EB07: Percentage (for coinsurance)

Real-Time vs. Batch Eligibility

Most clearinghouses support both modes:

  • Real-time (270/271): Send request, get response within 5-30 seconds. Used for front-desk eligibility checks at patient check-in. Requires HTTPS/API connectivity to clearinghouse
  • Batch (270/271): Upload a file of multiple 270 requests via SFTP, receive batch 271 response file within 1-24 hours. Used for pre-scheduling eligibility runs for the next day's appointments

For developers building eligibility verification into scheduling systems, the real-time approach via clearinghouse REST APIs is preferred. Most modern clearinghouses (Availity, Waystar, Change Healthcare) offer JSON-based APIs that abstract the X12 format while still returning the same data elements.

EDI 276/277: Claim Status

The 276 (Claim Status Inquiry) and 277 (Claim Status Response) transaction pair allows you to check the processing status of a submitted claim. This is the transaction behind every "claim status" button in a billing system.

276 Inquiry Structure

ST*276*0001*005010X212~
BHT*0010*13*STATUS_REQ_001*20260319*1000~
HL*1**20*1~
NM1*PR*2*BLUE CROSS BLUE SHIELD****PI*12345~
HL*2*1*21*1~
NM1*41*2*DR SMITH MEDICAL GROUP****46*123456789~
HL*3*2*22*0~
NM1*IL*1*DOE*JANE****MI*XYZ123456789~
DMG*D8*19850115*F~
TRN*1*PATIENT_ACCT_001*1234567890~
REF*D9*PAYER_CLM_001~
AMT*T3*250.00~
DTP*472*RD8*20260315-20260315~
SE*14*0001~

277 Response, Status Category Codes

The 277 response uses Status Category Codes (STC) that indicate where the claim is in the adjudication pipeline:

STC CodeCategoryMeaningDeveloper Action
A0AcknowledgmentForwarded to payer, acceptedNo action, wait for adjudication
A1AcknowledgmentReceipt acknowledgedMark as received by payer
A2AcceptanceAccepted for adjudicationUpdate status to "in processing"
A3RejectedRejected, needs correctionRoute to correction queue
P0PendingAdjudication pendingRe-check in 5-10 business days
P1PendingPending, additional info requestedAlert billing team, retrieve request
P2PendingPending, in reviewMonitor, no action needed yet
F0FinalizedPayment or denial finalizedExpect ERA (835) soon
F1FinalizedDeniedRoute to denial management
F2FinalizedPaidMatch to incoming ERA
R0-R16RequestAdditional information requestedRetrieve specific request, respond

For developers building claim tracking dashboards, the 276/277 cycle should be automated: poll claim status 14 days after submission, then every 7 days until finalized. Many billing systems implement this as a background job that runs nightly against all outstanding claims.

EDI 278: Prior Authorization

The 278 transaction set handles prior authorization (pre-certification) requests and responses. This is the electronic equivalent of calling a payer to request authorization for a procedure or referral.

278 Request Structure

The 278 request includes:

  • UM (Health Care Services Review Information), Request type (HS=Health Services Review), certification type (I=Initial, R=Renewal, A=Appeal), and service type
  • HCR (Health Care Services Review), In the response, contains the certification decision: A1=Certified, A2=Certified with modifications, A3=Not Certified, A6=Pended
  • SV1/SV2: Service lines being requested (same structure as 837 service lines)
  • DTP*472: Requested date range for the authorization
  • HI: Diagnosis codes supporting medical necessity
ST*278*0001*005010X217~
BHT*0007*11*AUTH_REQ_001*20260319*1000~
HL*1**20*1~
NM1*X3*2*BLUE CROSS BLUE SHIELD****PI*12345~
HL*2*1*21*1~
NM1*1P*2*DR SMITH MEDICAL GROUP****XX*1234567890~
HL*3*2*22*1~
NM1*IL*1*DOE*JANE****MI*XYZ123456789~
DMG*D8*19850115*F~
HL*4*3*EV*1~
UM*HS*I*3~
HI*ABK:M54.5~
DTP*472*RD8*20260401-20260430~
SV1*HC:72148*1200*UN*1~
SE*16*0001~

278 Response, Authorization Decisions

The authorization response returns in the HCR segment:

  • A1, Certified: Authorization granted. Extract the authorization number from REF*BB segment and store it for claim submission
  • A2, Certified with modifications: Authorized but with changes (different dates, fewer units). Review the modified SV1 segments
  • A3, Not certified: Denied. Route to appeal workflow. The HCR segment may include a denial reason
  • A6, Pended: Additional clinical information needed. Extract the request from the 278 response and trigger clinical documentation workflow

When building prior auth automation, store the authorization number (REF*BB) and certified date range. The authorization number must be included in the subsequent 837 claim submission in the REF*G1 segment; missing this is one of the most common claim rejection causes.

Parsing EDI in Code

Parsing X12 EDI files requires understanding the delimiter structure and segment hierarchy. Here is a practical Python approach.

Basic X12 Parser

import re
from typing import Dict, List, Optional
from dataclasses import dataclass, field


@dataclass
class Segment:
    segment_id: str
    elements: List[str]

    def element(self, index: int, default: str = "") -> str:
        if 0 < index < len(self.elements):
            return self.elements[index]
        return default

    def __repr__(self):
        return f"{self.segment_id}*{'*'.join(self.elements[1:])}"


class X12Parser:

    def __init__(self, raw: str):
        self.raw = raw.strip()
        self.element_sep = "*"
        self.segment_sep = "~"
        self.sub_element_sep = ":"
        self.segments: List[Segment] = []
        self._detect_delimiters()
        self._parse()

    def _detect_delimiters(self):
        if self.raw[:3] != "ISA":
            raise ValueError("Not a valid X12 file: must start with ISA")
        self.element_sep = self.raw[3]
        self.sub_element_sep = self.raw[104]
        self.segment_sep = self.raw[105]

    def _parse(self):
        clean = self.raw.replace("\n", "").replace("\r", "")
        raw_segments = clean.split(self.segment_sep)
        for raw_seg in raw_segments:
            raw_seg = raw_seg.strip()
            if not raw_seg:
                continue
            elements = raw_seg.split(self.element_sep)
            self.segments.append(Segment(
                segment_id=elements[0],
                elements=elements
            ))

    def get_segments(self, segment_id: str) -> List[Segment]:
        return [s for s in self.segments if s.segment_id == segment_id]

    def get_segment(self, segment_id: str) -> Optional[Segment]:
        matches = self.get_segments(segment_id)
        return matches[0] if matches else None

    def get_transaction_sets(self) -> List[List[Segment]]:
        sets = []
        current = []
        in_set = False
        for seg in self.segments:
            if seg.segment_id == "ST":
                in_set = True
                current = [seg]
            elif seg.segment_id == "SE":
                current.append(seg)
                sets.append(current)
                in_set = False
            elif in_set:
                current.append(seg)
        return sets


# Usage:
# parser = X12Parser(open("claim_837.edi").read())
# for seg in parser.get_segments("CLM"):
#     print(f"Claim: {seg.element(1)}, Amount: {seg.element(2)}")

835 ERA Parser, Extract Payments

@dataclass
class PaymentLine:
    procedure_code: str
    charged: float
    paid: float
    adjustments: List[Dict]
    service_date: str = ""


@dataclass
class ClaimPayment:
    patient_account: str
    status: str
    total_charged: float
    total_paid: float
    payer_claim_number: str
    lines: List[PaymentLine] = field(default_factory=list)
    adjustments: List[Dict] = field(default_factory=list)


def parse_835(raw_edi: str) -> List[ClaimPayment]:
    parser = X12Parser(raw_edi)
    claims = []
    current_claim = None
    current_line = None

    STATUS_MAP = {
        "1": "processed_primary",
        "2": "processed_secondary",
        "3": "processed_tertiary",
        "4": "denied",
        "19": "processed_primary_fwd_secondary",
        "22": "reversal",
    }

    for seg in parser.segments:
        if seg.segment_id == "CLP":
            if current_claim:
                claims.append(current_claim)
            current_claim = ClaimPayment(
                patient_account=seg.element(1),
                status=STATUS_MAP.get(seg.element(2), seg.element(2)),
                total_charged=float(seg.element(3, "0")),
                total_paid=float(seg.element(4, "0")),
                payer_claim_number=seg.element(7, ""),
            )
            current_line = None

        elif seg.segment_id == "SVC" and current_claim:
            proc_info = seg.element(1, "").split(":")
            proc_code = proc_info[1] if len(proc_info) > 1 else proc_info[0]
            current_line = PaymentLine(
                procedure_code=proc_code,
                charged=float(seg.element(2, "0")),
                paid=float(seg.element(3, "0")),
                adjustments=[],
            )
            current_claim.lines.append(current_line)

        elif seg.segment_id == "CAS":
            group = seg.element(1)  # CO, PR, OA, PI, CR
            adj_list = current_line.adjustments if current_line else (
                current_claim.adjustments if current_claim else []
            )
            # CAS can have up to 6 reason/amount pairs
            i = 2
            while i < len(seg.elements) - 1:
                reason = seg.element(i)
                amount = float(seg.element(i + 1, "0"))
                if reason:
                    adj_list.append({
                        "group": group,
                        "reason": reason,
                        "amount": amount,
                    })
                i += 3  # Each triplet: reason, amount, quantity

        elif seg.segment_id == "DTM" and seg.element(1) == "472":
            if current_line:
                current_line.service_date = seg.element(2, "")

    if current_claim:
        claims.append(current_claim)

    return claims

Useful Python Libraries

  • pyx12: Full X12 validation and parsing library. Validates against official X12 implementation guides. Open source. pip install pyx12
  • edi-835-parser: Focused 835 parser that extracts payment data into structured objects. pip install edi-835-parser
  • python-hl7: While primarily for HL7 v2, useful when bridging HL7 and X12 systems. pip install python-hl7
  • tigershark: X12 parser and generator. Generates Python classes from X12 implementation guides
  • stedi: Commercial API that handles EDI parsing, validation, and generation as a service. Good for teams that do not want to maintain parsers

EDI-to-FHIR Transformation

As healthcare moves toward FHIR-based interoperability, transforming EDI transactions to FHIR resources becomes critical. The FHIR Financial Module and the CARIN Alliance Blue Button Implementation Guide define the mappings.

837 CLM to FHIR Claim Resource

{
  "resourceType": "Claim",
  "identifier": [{
    "system": "https://provider.example.org/claims",
    "value": "PATIENT_ACCT_001"
  }],
  "status": "active",
  "type": {
    "coding": [{
      "system": "http://terminology.hl7.org/CodeSystem/claim-type",
      "code": "professional"
    }]
  },
  "patient": {
    "reference": "Patient/jane-doe"
  },
  "provider": {
    "reference": "Organization/dr-smith-medical"
  },
  "priority": {"coding": [{"code": "normal"}]},
  "diagnosis": [{
    "sequence": 1,
    "diagnosisCodeableConcept": {
      "coding": [{
        "system": "http://hl7.org/fhir/sid/icd-10-cm",
        "code": "J06.9"
      }]
    }
  }],
  "insurance": [{
    "sequence": 1,
    "focal": true,
    "coverage": {"reference": "Coverage/bcbs-xyz123"}
  }],
  "item": [{
    "sequence": 1,
    "productOrService": {
      "coding": [{
        "system": "http://www.ama-assn.org/go/cpt",
        "code": "99213"
      }]
    },
    "unitPrice": {"value": 125.00, "currency": "USD"},
    "quantity": {"value": 1}
  }],
  "total": {"value": 250.00, "currency": "USD"}
}

835 CLP/SVC/CAS to FHIR ExplanationOfBenefit

The ExplanationOfBenefit (EOB) resource is the FHIR equivalent of the 835 ERA. Each CLP claim maps to one EOB, with SVC lines mapping to EOB.item and CAS adjustments mapping to EOB.item.adjudication:

{
  "resourceType": "ExplanationOfBenefit",
  "identifier": [{
    "value": "PAYER_CLM_001"
  }],
  "status": "active",
  "type": {"coding": [{"code": "professional"}]},
  "outcome": "complete",
  "patient": {"reference": "Patient/jane-doe"},
  "insurer": {"reference": "Organization/bcbs"},
  "item": [{
    "sequence": 1,
    "productOrService": {
      "coding": [{"code": "99213"}]
    },
    "adjudication": [
      {
        "category": {"coding": [{"code": "submitted"}]},
        "amount": {"value": 125.00}
      },
      {
        "category": {"coding": [{"code": "benefit"}]},
        "amount": {"value": 100.00}
      },
      {
        "category": {"coding": [{"code": "deductible"}]},
        "amount": {"value": 0.00}
      },
      {
        "category": {"coding": [{"code": "copay"}]},
        "reason": {"coding": [{"code": "CO-45"}]},
        "amount": {"value": 25.00}
      }
    ]
  }],
  "total": [
    {"category": {"coding": [{"code": "submitted"}]}, "amount": {"value": 250.00}},
    {"category": {"coding": [{"code": "benefit"}]}, "amount": {"value": 125.00}}
  ],
  "payment": {
    "amount": {"value": 125.00}
  }
}

Key Mapping References

  • CARIN Blue Button IG: hl7.org/fhir/us/carin-bb, Defines the EOB profile for payer-to-consumer data exchange
  • Da Vinci PDex: hl7.org/fhir/us/davinci-pdex, Payer Data Exchange for payer-to-payer and payer-to-provider scenarios
  • Da Vinci PAS: hl7.org/fhir/us/davinci-pas, Prior Authorization Support using FHIR instead of 278
  • FHIR Financial Module: Core Claim, ClaimResponse, ExplanationOfBenefit, Coverage, and CoverageEligibilityRequest/Response resources

Clearinghouse Integration

Clearinghouses sit between providers and payers, handling routing, validation, format translation, and connectivity. Unless you are a very large health system with direct payer connections, you will connect to payers through a clearinghouse.

Availity

Availity is the largest multi-payer network with 95+ direct payer connections. Their integration options:

  • Availity Essentials API: RESTful JSON API with OAuth 2.0 authentication. Supports real-time 270/271, 276/277, and 278. Claims (837) are typically batch via SFTP. Documentation at developer.availity.com
  • SFTP Batch: Upload X12 flat files to your dedicated SFTP directory. Files are picked up, validated, and routed. Response files (835, 271, 277) appear in your download directory within hours
  • Availity API Marketplace: Third-party integrations built on Availity's connectivity. Useful for niche payer-specific workflows

Change Healthcare (Optum)

Change Healthcare processes 15 billion transactions annually. Post-acquisition by UnitedHealth Group (Optum), they are consolidating platforms:

  • ConnectCenter API: REST API for eligibility, claim status, and remittance. API key authentication. Supports both X12 and JSON payloads
  • Relay Health Exchange: SFTP-based file exchange for batch claims and remittance. Mature platform with extensive payer routing rules
  • FHIR APIs: Growing FHIR R4 support aligned with Da Vinci implementation guides. Primarily for payer-facing data exchange

Waystar

Waystar's platform processes claims for over 1 million providers:

  • Platform API v2: REST API with JWT authentication. Supports eligibility, claim status, prior auth, and denial intelligence. Webhook support for real-time event notifications
  • File Gateway: SFTP batch file exchange supporting X12 and Waystar proprietary formats. ERA auto-download capability
  • AltitudeAI Integration: API access to Waystar's AI features: predictive denial scoring, automated appeal generation, financial clearance recommendations

Choosing a Connection Method

For new integrations, follow this decision framework:

  1. Start with SFTP batch: It is the simplest to implement, and every clearinghouse supports it. Upload 837 files, download 835/271/277 files on a schedule
  2. Add real-time APIs for eligibility: The 270/271 workflow benefits most from real-time. Integrate the clearinghouse REST API for appointment-time eligibility checks
  3. Move claims to API when volume justifies: API-based claim submission gives you immediate validation feedback and claim tracking. Worth the investment at 500+ claims/month
  4. Implement webhooks for ERA: Instead of polling for 835 files, use webhooks to be notified when new ERAs are available. Reduces processing latency from hours to minutes

Testing EDI Transactions

Testing EDI integrations requires specialized environments, sample files, and validation tools. Here is the practical approach.

Test Environments

  • Clearinghouse sandboxes: Availity, Change Healthcare, and Waystar all offer sandbox environments with test payer IDs. Apply for developer access through their portals
  • CMS HETS: The HIPAA Eligibility Transaction System for testing Medicare/Medicaid 270/271 transactions. Available at cms.gov/CCIIO/Programs-and-Initiatives/Health-Insurance-Marketplaces/HETS
  • WEDI (Workgroup for Electronic Data Interchange): Industry resources and testing guidance at wedi.org

Sample Files

Generate valid test X12 files with:

  • X12 sample files from implementation guides: The official ASC X12 implementation guides (purchased from x12.org) include example transactions for each transaction set
  • Stedi EDI Inspector: Free online tool at edi.stedi.com that validates, formats, and generates sample X12 files
  • CMS sample files: CMS publishes sample 837, 835, and other transaction files for Medicare testing

Validation Tools

  1. EDI Notepad: Free Windows tool for viewing and editing X12 files with segment highlighting and element labeling
  2. Stedi EDI Inspector: Browser-based validator that checks X12 compliance against implementation guides. Free tier available
  3. pyx12 validator: Open-source Python library that validates X12 against implementation guides. Use for automated CI/CD validation of generated EDI files
  4. Edifecs: Enterprise-grade EDI testing platform used by major clearinghouses and payers

Unit Testing Strategy

import unittest


class TestEDI835Parser(unittest.TestCase):

    def setUp(self):
        with open("test_data/sample_835.edi") as f:
            self.raw_835 = f.read()

    def test_parse_payment_amount(self):
        claims = parse_835(self.raw_835)
        self.assertEqual(len(claims), 2)
        self.assertEqual(claims[0].total_paid, 125.00)
        self.assertEqual(claims[1].total_paid, 0.00)

    def test_denied_claim_status(self):
        claims = parse_835(self.raw_835)
        denied = [c for c in claims if c.status == "denied"]
        self.assertEqual(len(denied), 1)
        self.assertEqual(denied[0].patient_account, "PATIENT_ACCT_002")

    def test_adjustment_parsing(self):
        claims = parse_835(self.raw_835)
        line = claims[0].lines[0]
        co_adjs = [a for a in line.adjustments if a["group"] == "CO"]
        pr_adjs = [a for a in line.adjustments if a["group"] == "PR"]
        self.assertEqual(len(co_adjs), 1)
        self.assertEqual(co_adjs[0]["reason"], "45")
        self.assertEqual(co_adjs[0]["amount"], 25.00)
        self.assertEqual(len(pr_adjs), 1)
        self.assertEqual(pr_adjs[0]["amount"], 25.00)

    def test_claim_matching(self):
        claims = parse_835(self.raw_835)
        accounts = [c.patient_account for c in claims]
        self.assertIn("PATIENT_ACCT_001", accounts)

    def test_delimiter_detection(self):
        parser = X12Parser(self.raw_835)
        self.assertEqual(parser.element_sep, "*")
        self.assertEqual(parser.segment_sep, "~")

Common EDI Errors and Fixes

After years of building EDI integrations, these are the errors that cause the most pain and the fixes that save the most time.

999 Implementation Acknowledgment Errors

The 999 (or older 997) transaction is the acknowledgment that your EDI file was received and structurally valid. If you get a 999 with rejections, your file never made it to the payer; fix these first:

  • AK905 = R (Rejected): The entire transaction set was rejected. Check IK3/IK4 segments for specific segment and element errors
  • IK304 = 1 (Unrecognized Segment): You included a segment that does not exist in the implementation guide. Common cause: using 4010 segments in a 5010 file
  • IK304 = 3 (Required Segment Missing): A mandatory segment was omitted. Most common: missing NM1*85 (billing provider), missing HI (diagnosis), or missing DTP (service date)
  • IK403 = 1 (Required Element Missing): A mandatory data element within a segment is empty. Common: missing NPI in NM1*85, missing member ID in NM1*IL
  • IK403 = 6 (Invalid Character): X12 allows only alphanumeric characters and a limited set of special characters in most elements. Common culprits: curly quotes, em-dashes, non-ASCII characters from copy-paste

TA1 Interchange Errors

The TA1 is an interchange-level acknowledgment. If you get a TA1 rejection, the problem is in your ISA/IEA envelope; the clearinghouse could not even read the contents:

  • TA1-009 (Unknown receiver): Your ISA06 receiver ID is not registered with the clearinghouse. Contact the clearinghouse to register your sender/receiver trading partner agreement
  • TA1-014 (Invalid date): ISA09 must be in YYMMDD format. Double-check your date formatting
  • TA1-022 (Invalid characters): Non-allowed characters in the ISA envelope. The ISA segment has fixed-length fields; extra or missing characters shift everything

Top Claim-Level Rejections

These are the rejections that come back from the payer (not the clearinghouse). They mean your file structure was valid but the claim data has issues:

  1. Invalid/Missing NPI: Verify the billing provider NPI at npiregistry.cms.hhs.gov. Ensure the NPI is 10 digits and active. If the NPI is valid but still rejected, the provider may not be enrolled with that payer
  2. Subscriber ID mismatch: The member ID in NM1*IL does not match the payer's records. Run a 270/271 eligibility check before submitting claims to catch these early
  3. Duplicate claim: CLM segment patient account number + service date + procedure code matches an existing claim. Add a frequency code in CLM-05 for corrected claims (frequency 7 = replacement, 8 = void)
  4. Missing diagnosis pointer: SV1 service line references a diagnosis position that does not exist in the HI segment. Each SV1 diagnosis pointer must point to a valid HI line
  5. Timely filing: Claim was submitted past the payer's filing deadline. Filing limits vary: Medicare = 12 months, commercial payers = 90-365 days. No fix except prevention

Debugging Checklist

When an EDI transaction fails, follow this order:

  1. Check the 999/TA1 acknowledgment first; is it a structural or data issue?
  2. Use an EDI viewer (Stedi Inspector, EDI Notepad) to format and highlight the file
  3. Verify delimiters; is your segment terminator consistent throughout?
  4. Count your segments; does the SE segment count match the actual segment count?
  5. Validate control numbers, ISA13, GS06, and ST02 must be unique per file
  6. Check the implementation guide version; 005010 is current; submitting 004010 format will be rejected
  7. Compare against a known-good file; use a previously accepted transaction as a template
  8. Contact the clearinghouse support desk; they see these errors daily and can often identify the issue in minutes

If a rejection keeps coming back after these checks, discuss your claims integration with our integration team. Tell us your clearinghouse, the transaction types involved and the stage where the flow stops, and leave patient data out of what you send.

Two companion guides go deeper on production readiness: claim scrubber software for catching errors before submission, and EDI testing for clearinghouse integrations for proving the 999, 277CA and 835 handling works.

Production Implementation Checklist

Before going live with EDI transactions in production, verify every item on this checklist:

CategoryChecklist ItemNotes
Trading PartnersRegistered sender/receiver IDs with clearinghouseISA05/06 + ISA07/08 must be registered
Trading PartnersCompleted trading partner agreement (TPA)Legal document required by most clearinghouses
Trading PartnersTest transactions accepted in sandboxSubmit test 837, receive test 999/835
ConnectivitySFTP credentials + host verifiedTest upload/download cycle end to end
ConnectivityAPI credentials + OAuth flow testedFor real-time 270/271, 276/277
ConnectivityFirewall rules for clearinghouse IPsWhitelist both inbound and outbound
SecurityHIPAA BAA signed with clearinghouseRequired before transmitting PHI
SecurityTLS 1.2+ enforced on all connectionsNo unencrypted EDI transmission
SecurityPHI logging controls in placeDo not log full EDI content in plain text
ValidationOutbound 837 validation against 5010 IGUse pyx12 or Stedi for pre-submission validation
ValidationInbound 835 parser tested with 10+ real ERAsCover all CAS group/reason code combos
Validation999/TA1 processing automatedAuto-route rejections to correction queue
OperationsMonitoring + alerting on failed transactionsAlert within 1 hour of 999 rejection
OperationsRetry logic for transient failuresExponential backoff, max 3 retries
OperationsArchival of all sent/received EDI filesHIPAA requires 6-year retention

 

Working with real files? Our free EDI Inspector decodes 835/837/277 in the browser: hover any element for the X12 reference, with a searchable code dictionary (⌘K).

Where is your claims integration getting stuck?

Tell us your clearinghouse, transaction types and the stage that is blocking you. Discuss acknowledgment handling, retries, claim-status tracking and remittance reconciliation with our integration team.

Frequently Asked Questions

What is EDI in healthcare?

Healthcare EDI (Electronic Data Interchange) is the standardized format for exchanging administrative and financial data between providers, payers, and clearinghouses, maintained by ASC X12 and mandated by HIPAA since 1996. Over $4 trillion in US healthcare spending flows through EDI transactions annually. The current version is 005010, and every transaction follows a hierarchical envelope structure of ISA/IEA, GS/GE, and ST/SE segments with delimiter-separated data elements.

What is the EDI 837 transaction set?

The EDI 837 is the electronic healthcare claim, the equivalent of the CMS-1500 professional or UB-04 institutional paper form. It carries everything a payer needs to adjudicate a claim: patient demographics, provider NPIs, ICD-10 diagnosis codes, CPT/HCPCS procedure codes, and charges. It comes in three variants, 837P for professional claims, 837I for institutional claims, and 837D for dental claims, each with its own X12 implementation guide.

What is an EDI 835 remittance advice (ERA)?

The EDI 835, or Electronic Remittance Advice, is the transaction a payer sends back to explain exactly what was paid, what was adjusted, why it was adjusted, and what the patient owes. It is the transaction that drives payment posting automation in every practice management and billing system, with the BPR segment carrying the payment method and total amount and adjustment reason codes explaining each reduction.

What is the difference between an 835 and an 837?

An 837 is the electronic claim a provider sends to a payer to request payment. An 835 is the payer's electronic remittance advice (ERA) sent back after adjudication, showing what was paid, each adjustment with its reason code, and the patient's responsibility. The 837 flows provider to payer; the 835 flows payer to provider, and it links back to the claim through CLP01, which repeats the patient control number from CLM01.

What is the difference between 837P and 837I claims?

The 837P covers professional claims from physicians, outpatient clinics, and ambulatory surgery centers, using SV1 service lines keyed on CPT/HCPCS codes. The 837I covers institutional claims from hospitals, skilled nursing facilities, and home health agencies, replacing SV1 with SV2 segments keyed on revenue codes, adding the CL1 segment for admission and patient status codes, and embedding a 3-digit Type of Bill code in the CLM segment.

How do developers build healthcare claims integration with EDI?

Claims integration means parsing and generating the six core X12 transaction sets, 837 claims, 835 remittance, 270/271 eligibility, 276/277 claim status, and 278 prior authorization, then connecting them through a clearinghouse integration architecture. Production work also involves mapping EDI data to FHIR and handling vendor-specific quirks no specification covers. Healthcare engineering teams like Nirmitee.io build these payer integrations, from segment parsing through posting automation.

How does Nirmitee.io approach healthcare software development?

Nirmitee.io follows a healthcare-first engineering approach — every solution is built with HIPAA compliance, HL7/FHIR interoperability standards, and clinical workflow requirements at the core. We combine domain expertise with modern engineering practices to deliver production-ready systems.

Why does an 837 claim get rejected?

Most 837 rejections come from data errors: a claim total that does not equal the sum of the line charges, invalid or missing NPIs, invalid dates, wrong payer or subscriber identifiers, missing diagnosis pointers and codes that do not match the payer's edits. Syntax problems come back on the 999 acknowledgment; business-rule rejections come back on the 277CA.
Share