The blog  ·  2026-07-31

Every wire format is a dialect. The transaction is the noun.

Written for ED-3 — the in-house supply-chain platform team — the staff engineer who owns the event spine and refuses to own three document stores.

One thesis: your purchase order does not care whether it traveled as an X12 850, an EDIFACT ORDERS, or a REST webhook — those are dialects, and the compile target for all three is the same CBV bizTransaction reference on the same EPCIS 2.0 event.

The claim, stated as an engineer would state it

A purchase order is a noun in your business. On the wire it has at least three costumes: a 4010 850 from the retail account that still runs a VAN, an EDIFACT ORDERS from the European distributor, and a JSON body from the commerce platform's webhook. Most stacks respond by building three ingest paths into three tables, then a reconciliation layer to convince themselves the three tables describe one world.

The standard already refused that design. CBV 2.0 §8.5 defines bizTransactionList as the field that populates the "why" dimension of an event — and defines it on all EPCIS event types. The vocabulary of §7.3 gives the values that go in it: po, desadv, inv, recadv, and the rest of a deliberately short list written to be an EDI catalog, not a metaphor for one. The document is not the record. The document is context on the record.

The compile target is pinned, not asserted

The proof artifact for this post is not prose; it is a file you can hash. The official EPCIS 2.0 JSON-LD context — vendored and pinned at sha256 5056c65f991425b1d3a35e35edf4f7d0c7ff56cf688c2912b930f93494713737 (retrieved 2026-07-19 from ref.gs1.org, per the family's pins discipline) — carries the business-transaction type sub-context verbatim:

"desadv": "cbv:BTT-desadv",
"inv":    "cbv:BTT-inv",
"po":     "cbv:BTT-po",
"recadv": "cbv:BTT-recadv"

Those four are the core of the cycle — order, despatch advice, invoice, receiving advice — and the same pinned sub-context enumerates the rest of the vocabulary (bol, cert, pedigree, poc, prodorder, rma, testprd, testres, upevt). The close reading of that enumeration is its own post. What matters here: the target vocabulary is small, closed, and owned by the standard — not by transactions.dev, and not by you.

One purchase order, three arrivals, one reference

Here is the figure that makes the thesis concrete. The same order, PO-8812, issued by a buyer whose GLN is 0724142000024's parent party 0724142000017, arrives three ways:

X12 856 (the ASN citing the order)   PRF*PO-8812
EDIFACT / EANCOM                     RFF+ON:PO-8812
biztx-canonical JSON (API-native)    { "kind": "po", "value": "PO-8812" }

All three compile to one and the same entry on the emitted event:

"bizTransactionList": [
  {
    "type": "po",
    "bizTransaction": "urn:epcglobal:cbv:bt:0724142000017:PO-8812"
  }
]

That identifier shape is CBV 2.0 §8.5's own URN scheme — urn:epcglobal:cbv:bt:{issuerGLN}:{reference} — scoped by the party that assigned the number, which is what makes the reference equal across counterparties without a mapping table. The API-native shape above is not a sketch: it is the published biztx-canonical schema's references entry, and the X12 path is exercised by the same golden fixture pair, byte for byte.

Why the event is the join point

The alternative architecture — a document store beside an event store, with a nightly job that joins them — fails in a specific, predictable way: the join key lives in application code, and application code drifts. Putting the reference on the event means every downstream consumer that can parse a conformant EPCIS document gets the paperwork context for free, because the field it looks in is defined by the standard it already implements.

The emitted record stays conformant and, where a source document carries more than the standard models, a superset — the two words travel together here, always, because a superset that stops validating against the pinned official schema is just a private format with a standards story. Every event this pipeline emits validates against the pinned EPCIS 2.0 JSON schema (sha256 0f46ff69…) before it leaves the tool; the projection down to the plain conformant document is lossy in one direction only.

What each door in the family does with the noun

  • epcis.dev is the event spine: capture, validate, hash, translate. It treats bizTransactionList as what it is — a core field.
  • transactions.dev — this door — is the compiler: parse takes the dialect (X12, EDIFACT, canonical JSON), join emits the conformant events with the references, parties, and item grain already placed.
  • barcoding.dev is the identifier layer: the GTINs, GLNs, and SSCCs that make the joins deterministic instead of fuzzy.
  • visibility.cloud is the executive twin of this post: the same architecture, read side out, in the light register.

Where this goes next

The worked end-to-end of one document class — a full ASN in, a validated event skeleton out, every byte accounted for — is the 856 compile post. The naming law that ends the ASN argument is the desadv post. And the spec's own answer to "which events can carry the reference" — all of them — is the §8.5 post.

If the thesis matches a problem you own — three dialects on the wire, one record you have to defend — the door is get access: say what you compile today, and the onboarding flow takes it from there.


847 words. The access flow is at /get-access/; the family's event door is epcis.dev.