blog · 2026-07-31 · written for: ED-1 — the Integration Engineer at a supply-chain ISV; a decade of vendor EDI and XML; evaluates by running, never by meeting

The ASN knows everything about the shipment except who shipped it

One thesis: the despatch advice and the event stream describe the same physical act from two directions, join on spec-sanctioned fields — and share the same structural silence: neither has a field for the performer.

If you have spent years wiring 856s into WMS and ERP systems, you already know the document is underrated. An ASN is the richest identity-bearing artifact in the commercial exchange: it names the logistic units (SSCC), the products (GTIN), the parties (GLN), the purchase order, and the ship date. What it has never carried — and what EPCIS, the standard built to record the physical side, also cannot carry — is who did the work. This post walks the join, then the silence.

The HL tree, in our own words

An 856 organizes its content as a hierarchy of HL segments: each carries its own id, its parent's id, and a level code — shipment, order, tare, pack, item. It is a runtime parent-pointer tree, not a static grammar: the document declares its own shape as it goes, which is why naive segment-order parsers break on it and why every serious 856 implementation builds the tree first and reads meaning off it second. (For segment-by-segment element detail, Stedi's X12 856 reference is the best public resource; we link it rather than reproduce it — X12's text is licensed, and our prose here is our own.)

Here is a minimal self-authored synthetic 856 — one shipment, one order, one pallet, one item:

ST*856*0001~
BSN*00*S2607311420*20260731*1420~
HL*1**S~
DTM*011*20260731~
N1*SF*ACME FOODS*UL*4098765000006~
N1*ST*RETAIL DC 12*UL*0614141000005~
HL*2*1*O~
PRF*PO-4711~
HL*3*2*P~
MAN*GM*006141411234567890~
HL*4*3*I~
LIN**UP*614141007349~
SN1**24*CA~
SE*14*0001~

The four joins, all spec-sanctioned

Every load-bearing value in that document has a first-class home in an EPCIS 2.0 event, defined in the standard's core — not in anyone's custom extension:

856 carriesEPCIS 2.0 homeSanction
MAN GM value (SSCC 006141411234567890)AggregationEvent parentIDCBV 2.0 §8.5
LIN UP value (GTIN, with SN1 quantity)epcList (serialized) or quantityList (lot/quantity grain)EPCIS core
BSN02 shipment id · PRF PO numberbizTransactionList as desadv / poCBV 2.0 §7.3
N1 SF/ST GLNs (N104 qualifier UL)sourceList / destinationList (owning_party, location)CBV 2.0 §7.4

The derived event set from the document above: one AggregationEvent (pallet urn:epc:id:sscc:0614141.0123456789 gains its 24 cases) and one shipping ObjectEvent — bizStep shipping, disposition in_transit, DTM 011 as eventTime, both bizTransaction references, both GLN party lists. Every generated event validates against epcis.dev's pinned official EPCIS 2.0.1 schema before it counts as done; the fixture pairs (.edi in, .expected.epcis.json out) are the acceptance oracle, and the mapping table itself is pinned data, changed only by journaled ruling. Run the round trip yourself — that is the evaluation.

Now look at what neither side can say

Trace the physical act behind that document. Someone — a picker, a forklift driver, a palletizing robot, an agent orchestrating a wave — built that pallet and staged that shipment. Ask both records who it was:

  • The 856 has no answer. Its party segments are organisation-grain: GLNs naming the ship-from and ship-to legal entities and locations. There is no segment whose meaning is the person or agent who performed the handling. The contact segments name administrative contacts for the document, not performers of the work.
  • The EPCIS event has no answer either. Five dimensions, no performer, organisation-grain party fields — the section-numbered proof is walked once, in the arc's opening post. Which worker, which agent, at the moment of the act — structurally absent. Not blank; absent. There is no field to leave blank.

So after the cleanest possible join — every SSCC, GTIN, GLN, PO and shipment reference landing in its sanctioned slot — you hold a record that can prove what shipped, when, from which company's site to which company's site, against which order — and cannot name a single hand or agent that touched it. For an ISV whose customers are starting to ask exactly that question (a chargeback dispute, a temperature excursion, a recall investigation: who built this pallet?), that silence is not a data-quality gap to be cleaned up. It is a wire-format fact on both sides of the wire, and no amount of mapping fixes it.

The family's answer lives one layer up: the performer carried as an attested who on the event — distinct, always, from capturedBy, the warrantor account that posted it — via the namespaced extension path EPCIS itself sanctions. Conformant to the standard; explicit about everything beyond it. The identity grains it rides on are the ones this post just joined: the same GLN that appears in your N1 loop, in the event's source list, and in a Peppol endpoint id is the party key across all three layers — see /schemes/gln for that three-way join stated as reference material.

Where this runs

The 856→EPCIS join pipeline — parse, tree, map, validate, emit — is part of the transaction layer the family builds under transactions.dev, whose thesis is this post's in one line: the trace carries its paperwork. The identity verbs underneath it (resolve and verify on every SSCC, GTIN and GLN the documents carry) run locally and free. The pipeline itself is gated; if a decade of vendor formats is your day job and the mapping table above is the artifact you want to run against your own corpus, start here — the survey asks about your document mix so the answer fits it.

Keys open the network half of the verb set. Get an API key — say what arrives at your door, and the provisioning order follows the list.

We answer in writing. We take at most five conversations a month, only when you ask for one, and only after you already have the written read.