# An 856 is an aggregation tree wearing a trench coat.

Strip the envelope off an X12 856 and what remains is a tree. The despatch advice describes a shipment that contains orders, orders that contain packs, packs that contain items — a rooted hierarchy the message spells out one node at a time, each node naming its parent. That is also, exactly, the shape of the EPCIS aggregation model: an SSCC parent, its children, and the events that assert containment. The mapping between the two is not a heuristic. It is deterministic, and this post walks it on a worked pair.

## The tree, in original prose

The 856 builds its hierarchy with a repeating structural segment: each occurrence carries its own sequence number, a pointer to its parent's number, and a level code — shipment, order, tare, pack, or item. Reading the segments in order and following the parent pointers reconstructs the tree; nothing about the nesting is implicit. Pack-level nodes carry the marks physically applied to the freight — in GS1 practice, the SSCC of the pallet or case. Item-level nodes carry the trade item and its quantity: the GTIN, and where serialization applies, the serials; where lot-tracking applies, the lot.

That is all the structure this mapping needs, stated in our own words. For element-by-element segment detail, use [Stedi's X12 856 reference](https://www.stedi.com/edi/x12-004010/856) — the canonical public reference, credited here and linked, never copied. (Why we link instead of republishing is [its own post](/x12-link-dont-copy/).)

## A self-authored fixture

This wire document is synthetic — authored for this corpus, no partner's implementation guide reproduced:

```
ST*856*0001~
BSN*00*5501*20260729*0712~
HL*1**S~
DTM*011*20260729~
REF*BM*80144100~
N1*SF*ACME FOODS*UL*0614141007776~
N1*ST*NORTHDOCK DC*UL*0614142008888~
HL*2*1*O~
PRF*PO-06012-1234~
HL*3*2*P~
MAN*GM*006141410000055012~
HL*4*3*I~
LIN**UP*614141073467*LT*7A-4412~
SN1**96*EA~
CTT*4~
SE*16*0001~
```

One shipment, one order, one pallet, one lot-tracked line: 96 each of GTIN 614141073467, lot `7A-4412`, on SSCC 006141410000055012, shipped 2026-07-29 against PO-06012-1234, ship-from and ship-to identified by GLN.

## The mapping table

| ASN fact | source | EPCIS landing zone |
|---|---|---|
| shipment node | root of the hierarchy | shipping `ObjectEvent`, `bizStep: shipping`, `disposition: in_transit` |
| pack node + mark | pack level, SSCC in the marks segment | `AggregationEvent`, `parentID` = SSCC EPC URI |
| item node, serialized | item level, GTIN + serials | `childEPCs` as SGTIN URIs |
| item node, lot-grain | item level, GTIN + lot + quantity | `childQuantityList` as LGTIN + quantity + UoM |
| ASN number | BSN02 | `bizTransactionList: { "type": "desadv", … }` |
| PO number | PRF | `bizTransactionList: { "type": "po", … }` |
| bill of lading | REF (BM) | `bizTransactionList: { "type": "bol", … }` |
| ship-from / ship-to GLNs | party loop, GLN qualifier | `sourceList` / `destinationList` (`owning_party`, `location`) |
| despatch date | DTM (011) | `eventTime` of the shipping event |

Every row lands on a core field. No extensions, no invented vocabulary — this is the machinery [CBV built for exactly this document set](/epcis-biztransaction-po-desadv/).

## The output, validated

`biztx join` emits the event skeleton; here is the expected-output fixture for the document above, from the corpus pair. The aggregation event:

```json
{
  "type": "AggregationEvent",
  "parentID": "urn:epc:id:sscc:0614141.0000005501",
  "childQuantityList": [
    { "epcClass": "urn:epc:class:lgtin:0614141.007346.7A-4412",
      "quantity": 96, "uom": "EA" }
  ],
  "action": "ADD",
  "bizStep": "packing",
  "bizTransactionList": [
    { "type": "desadv",
      "bizTransaction": "urn:epc:id:gdti:0614141.11711.5501" }
  ]
}
```

and the shipping event carries the SSCC in `epcList`, all three document references, and both parties. The whole pair validates against the pinned official schema:

```
$ npx epcis.dev validate 856-expected.epcis.json
valid	schema=epcis-json-schema.json	version=2.0.1
$ npx epcis.dev hash 856-expected.epcis.json
hash	0	ni:///sha-256;a10cc2beddbd2cc3757d1efd00e9a5359cb9734440678d946dd6974e69a7da74?ver=CBV2.0
hash	1	ni:///sha-256;2ae830781eb07477910e75430daea6c1bea8fd04ad44bd1ac168e8b1daa8d8b0?ver=CBV2.0
```

Validation is the acceptance gate: an event the pinned EPCIS 2.0.1 schema rejects never leaves the join. And because identity is the CBV §8.9 content hash, the same ASN mapped twice produces the same events — replays cost nothing.

## What the mapping refuses to invent

A deterministic mapping is honest about what the document does not know. An 856 asserts what the shipper *says* was packed and shipped — so the join emits `packing` and `shipping` events with the despatch date the document itself carries, and it stops there. It does not fabricate a receiving event, a commissioning time, or a business step the document never witnessed. Missing quantities, an orphaned parent pointer, a cycle in the hierarchy: each is a typed rejection naming the node, never a silently pruned tree. The events that say what *actually* arrived come from your scanners — and diffing the two is [the entire receiving story](/reconcile-asn-against-events/).

The EDIFACT twin of this mapping — DESADV, at full open-content depth — is [here](/edifact-desadv-to-epcis/), CPS packing tree and all.

**Validate the fixture pair now** with `npx epcis.dev validate` — no account, exit code is the verdict. **Run your own ASNs through the join:** the document verbs ride the capture surface — **[get a capture key](/get-a-key/)**.

---

**Proof, not adjectives.** This build could not verify the gateway's test suite green, so no sentence on this page claims a passing run — the last verified run is recorded in the repository's own CI, not here. Live on this origin, no key: POST /translate, /validate, /hash. No conformance attestation has ever been issued. The dated ledger is /what-ships-today/.
