blog · 2026-07-31 · written for: ED-3 — the in-house supply-chain platform team (the platform / staff engineer wiring scan capture into an event repository); B2D worked example

From scan to conformant event: constructing the ObjectEvent

One thesis: the path from a parsed 2D payload to an EPCIS 2.0 ObjectEvent is deterministic — every AI has exactly one destination field, the scanning context supplies the rest — and the event is not done until it validates against the pinned official schema. The mapping fits in one table, the grain rule fits in one sentence, and the exit gate is a machine verdict rather than a code review. If your platform team is wiring scanners into an event repository, this is the whole function, published.

The mapping table

Input: the canonical identity resolve produces — scheme, canonical id, parsed AI map — plus the scanning context your capture point already knows about itself. Output: a valid ObjectEvent. The mapping:

SourceEPCIS fieldRule
(01) + (21) — GTIN + serialepcListas SGTIN EPC URI — an instance, listed individually
(01) + (10) — GTIN + lot, no serialquantityListas LGTIN epcClass + quantity + uom — a class, counted
(01) alonequantityListclass-level GTIN pattern + count — the least you can honestly say
(00) — SSCCepcList (ObjectEvent) / parentID (AggregationEvent)the logistic unit; parent the moment containment is asserted
(17), (10) as dataILMD block, on commissioning eventslot/expiry are instance/lot master data, carried where the standard puts them
device clockeventTime + eventTimeZoneOffsetoffset is mandatory — capture it at the edge, never reconstruct it later
capture pointreadPoint.idthe SGLN of where the scan physically happened
process contextbizStep / dispositionCBV vocabulary values — receiving, in_progress, not free text
document contextbizTransactionListtyped refs (po, desadv) joining the event to its paperwork
observation intentactionOBSERVE for a passing scan; ADD only when commissioning

The one structural decision in the whole function is row one versus row two — grain routing: serial present means epcList (this unit, traceable), lot-only means quantityList (this batch, countable). Get that wrong and every downstream trace query is wrong in a way no validator catches, because both forms are schema-valid. The grain consequences are worked in Serial vs lot; the construction rule here is mechanical: (21) decides.

The worked construction

Payload off a case scanner at a receiving dock, through the function:

]d2010950600013435210AB12<GS>2140614

resolve gives GTIN 09506000134352, lot AB12, serial 40614 — serial present, so epcList, as an SGTIN. Context supplies the rest:

{
  "type": "ObjectEvent",
  "eventTime": "2026-07-31T14:20:07.00Z",
  "eventTimeZoneOffset": "-06:00",
  "epcList": ["urn:epc:id:sgtin:9506000.013435.40614"],
  "action": "OBSERVE",
  "bizStep": "receiving",
  "disposition": "in_progress",
  "readPoint": { "id": "urn:epc:id:sgln:9506000.00012.0" },
  "bizTransactionList": [
    { "type": "po", "bizTransaction": "urn:epcglobal:cbv:bt:9506000000018:PO-4711" }
  ]
}

Note what the construction did not invent: no product name, no trading-partner enrichment, no inferred fields. An event is testimony about an observation; everything else lives in other layers, joined by identifiers. The bizTransactionList entry is the join to the transaction layer — the PO lives on transactions.dev's side of the family, referenced, never copied.

The exit gate: validation, then the hash

The event above is not done because it looks right. It is done when two machine verdicts say so.

Gate one — schema validation. The event validates against epcis.dev's pinned copy of the official EPCIS 2.0.1 schema — pinned meaning the artifact's sha256 is published and changes only by journaled ruling, so "valid" is a claim against a specific, citable normative object, not against whatever a validator bundled last quarter. Events that fail do not leave the function. There is no "log it anyway" branch, because an event repository with invalid events is a data-quality project wearing a traceability costume.

Gate two — the event hash. CBV 2.0 §8.9 defines a canonical hashing algorithm for EPCIS events: a specified canonicalization of the event, hashed, yielding an identity for the event itself. The construction computes it, and the implementation is gated against the OpenEPCIS reference vectors — the published test cases the community treats as ground truth — so two independent systems hashing this event agree. That agreement is what makes the hash useful: it is a portable, vendor-neutral name for "exactly this testimony," the primitive that de-duplication, citation, and later verification all build on.

Deterministic mapping in, schema verdict and reference-gated hash out. That is the entire function, and every piece of it is checkable: the mapping table against the specs, the schema against its digest, the hash against public vectors.

The field that isn't there

One closing block, because the construction makes it visible. Walk the event again: what (epcList), when (eventTime), where (readPoint), why (bizStep, disposition, the transaction list), how (the sensor/ILMD machinery when present). Five dimensions — and no field for who performed the observation. Not an empty field: no field. The standard's party grain is the organization; the person, agent, or machine that actually scanned has nowhere to land.

The family carries it conformantly — a namespaced extension holding who, the attested observer, distinct always from capturedBy, the warrantor account that posted the event; the event above still validates with the extension present, and the two grains never collapse, because who saw it and who vouches for the record are different questions with different answers. The full argument — why the absence is structural and what the extension path is — is the wedge post of this arc's beginning; the identity grain itself is id.org.ai's: Agent. Human. Thing.

Build the function against your own payloads: npx barcoding.dev resolve gives you the AI map today, pure and local; the constructed event validates with epcis.dev's own tooling. Durable capture — the stream those events land in — is provisioned with API access: get an API key, and name the capture points you run.

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.