Skip to main content
Version: v0.0.2

The 80/20 transaction sets

X12 defines hundreds of transaction sets; HIPAA mandates a small handful for healthcare, and a smaller handful again carry the overwhelming majority of real integration traffic. @cosyte/x12 v1 covers exactly the HIPAA 005010 healthcare sets (each with a lenient reader and a spec-clean domain builder) and nothing else. This page is the map: what each set is, which function reads it, which builds it, and the one field each one preserves verbatim because getting it wrong causes harm.

Depth tracks the code. Every function named below is a shipped export. Where a set has a read-side limitation (e.g. 837 claim-/line-level provider addresses), it is called out in Troubleshooting & known limitations, not glossed over.

The map

SetWhat it isReadBuildPreserved verbatim
270 / 271Eligibility inquiry / responseget271Eligibilitybuild271the 270's TRN-02 trace, echoed onto the 271 (reassociation)
276 / 277Claim status inquiry / responseget277Statusbuild277the 276's trace; the STC category/status/entity triple
277CAClaim acknowledgmentget277CADispositionbuild277CAper-claim accept/reject disposition + your submitted trace
278Services review request / responseget278Request / get278Responsebuild278Request / build278Responsethe HCR-01 certification action (response), never inferred
820Premium paymentget820Paymentsbuild820monetary amounts (emitted as-is; no balance equation)
834Benefit enrollment & maintenanceget834Header / get834Enrollmentsbuild834the INS-03 / HD-01 maintenance-type code (X12 0875)
835Claim payment / advice (ERA)get835build835every monetary field; the balance is checked, never rebalanced
837P / 837I / 837DProfessional / institutional / dental claimsget837Claimsbuild837P / build837I / build837Dthe HL hierarchy; HI diagnosis qualifier → code system
999Implementation acknowledgmentparse999build999per-segment / per-element syntax error notes
TA1Interchange acknowledgmentparseTA1buildTA1the interchange acknowledgment + note codes

Routing: which set is this?

An interchange can carry multiple functional groups and transaction sets. Route on GS-01 (the functional identifier) and ST-01 (the transaction set ID), then hand the transaction to the matching reader:

import { parseX12, get835 } from "@cosyte/x12";

const raw =
"ISA*00* *00* *ZZ*MEDPAY *ZZ*CLINIC001 " +
"*260601*1200*^*00501*000000001*0*P*:~" +
"GS*HP*MEDPAY*CLINIC001*20260601*1200*1*X*005010X221A1~" +
"ST*835*0001~" +
"BPR*I*450.00*C*ACH*CCP*01*021000021*DA*1234567*1512345678**01*021000021*DA*98765*20260601~" +
"TRN*1*0012345*1512345678~" +
"SE*3*0001~GE*1*1~IEA*1*000000001~";

const ix = parseX12(raw);
const group = ix.groups[0];

group?.gs.elements[1]; // => "HP"

// Find the 835 transaction and decode it. A reader returns `undefined` for a
// mis-routed transaction, so this pattern is safe across mixed interchanges.
const tx = group?.transactions.find((t) => t.st.elements[1] === "835");
const remit = tx ? get835(ix.delimiters, tx) : undefined;

remit?.traces[0]?.referenceId; // => "0012345"

The reader/builder symmetry

Every builder round-trips through its reader: get835(parseX12(serializeX12(build835(spec)))) reproduces the spec field-for-field. That symmetry is the correctness contract: a builder that emitted something its own reader could not read back would be caught by the round-trip property tests. The builders are pure functions: they never auto-send, open a socket, or touch the filesystem, and they refuse a structurally impossible spec with a typed error rather than emitting corruption.

What is out of scope (v1)

  • Non-healthcare sets: 850 (purchase order), 856 (ASN), 810 (invoice), 204 (load tender), etc.
  • The EDIFACT syntax family: a different standard entirely.
  • Transport: AS2, SFTP, MLLP-style delivery. This is a parser/serializer, not a comms stack.
  • Pre-005010 field maps. Pre-005010 input is tolerated and flagged (X12_PRE_005010), not decoded against those older guides.

See Troubleshooting & known limitations for the full non-goals list.