The launch journal

Inputs, outputs, and the lot that became ten.

31 July 2026 · the launch journal

If your product is cooked, blended, cut, or repacked, the question that ends careers is not "where did the pallet go." It is "which finished lots contain that input." Most systems answer it with a genealogy database — a side system that joins batch records to consumption records, maintained by the plant, reconciled by hand, and unavailable the moment the question crosses a company boundary.

The standard already has the answer, and it is not a database. It is an event type. TransformationEvent is the one EPCIS event where what comes out is not what went in: inputEPCList/inputQuantityList name what was consumed, outputEPCList/outputQuantityList name what was produced, and the event asserts the causal link between them. Record production that way and genealogy stops being a system you maintain. It is a query over events you already captured.

The worked fixture: two lots in, three lots out

A co-manufacturing run: two raw-material lots — 620 kg of one ingredient, 180 kg of another — consumed into one production run that fills three finished-good lots. One conformant event:

{
  "type": "TransformationEvent",
  "eventTime": "2026-07-31T06:10:00.000Z",
  "eventTimeZoneOffset": "-05:00",
  "inputQuantityList": [
    { "epcClass": "urn:epc:class:lgtin:0614141.011111.LOT-RM-4411", "quantity": 620, "uom": "KGM" },
    { "epcClass": "urn:epc:class:lgtin:4012345.022222.LOT-RM-8802", "quantity": 180, "uom": "KGM" }
  ],
  "outputQuantityList": [
    { "epcClass": "urn:epc:class:lgtin:0614141.077777.TLC-26212-A", "quantity": 2400, "uom": "EA" },
    { "epcClass": "urn:epc:class:lgtin:0614141.077777.TLC-26212-B", "quantity": 2400, "uom": "EA" },
    { "epcClass": "urn:epc:class:lgtin:0614141.077777.TLC-26212-C", "quantity": 1180, "uom": "EA" }
  ],
  "transformationID": "urn:epc:id:gdti:0614141.54321.RUN-26212",
  "bizStep": "commissioning",
  "disposition": "in_progress",
  "readPoint": { "id": "urn:epc:id:sgln:0614141.00777.KETTLE-2" },
  "bizLocation": { "id": "urn:epc:id:sgln:0614141.00777.0" },
  "ilmd": {
    "cbvmda:lotNumber": "TLC-26212",
    "cbvmda:productionDate": "2026-07-31",
    "cbvmda:bestBeforeDate": "2027-01-27"
  }
}

Validated against the pinned official GS1 schema, on this machine, before it appears here:

$ npx epcis.dev validate transformation-two-in-three-out.json
valid	schema=epcis-json-schema.json	version=2.0.1
$ npx epcis.dev hash transformation-two-in-three-out.json
hash	0	ni:///sha-256;38222f4a9c7815c6868f7fe1326d25385d40af350e9977e7b6e568823a2456c1?ver=CBV2.0

That second line matters: the event's identity is the CBV 2.0 §8.9 hash of its own content, so your record of the run and your co-manufacturer's record of the same run carry the same identity with no coordination. The schema pin (sha256, retrieval date, source URL) is published on the conformance page.

Reading the fixture like a reviewer

Quantity lists, not EPC lists, because the grain is the lot. Bulk ingredients are not serialized; inputQuantityList carries class-level LGTIN identifiers with quantity and unit (KGM — UN/CEFACT common codes, the standard's unit vocabulary). If your line serializes finished units, outputs go in outputEPCList as SGTINs instead. The event type does not force a grain; the physical process picks it.

transformationID is the run's thread. A production run that spans a shift change — or a clean-in-place cycle, or a day — is rarely one observation. The standard's answer is to capture multiple TransformationEvents sharing one transformationID: the 06:10 event carries the morning's inputs and the first two output lots, the 14:40 event the afternoon's, and any query that resolves genealogy treats every event bearing that ID as one transformation. Inputs in one session link to outputs in another through the ID, which is exactly what a batch that outlives a shift needs. Here it is a GDTI naming the batch record; any URI your quality system already mints works.

ILMD is where the new lot is born. The output lots did not exist before this event, so their master data — lot number, production date, best-before — is stamped here, in ilmd, using CBV master-data attribute names (cbvmda: — the CBV's instance/lot master data vocabulary). Writable at creation, immutable after, traveling with the record. No side table, because side tables do not travel across company boundaries.

Genealogy as two queries

The EPCIS query interface has transformation-shaped predicates as first-class parameters — MATCH_inputEPC, MATCH_outputEPC, MATCH_inputEPCClass, MATCH_outputEPCClass, EQ_transformationID. Both directions of the career-ending question compile to one call each:

Backward — "what went into this finished lot?" Query MATCH_outputEPCClass for lgtin:...TLC-26212-B. The result names LOT-RM-4411 and LOT-RM-8802, with quantities, time, kettle, and site. Recurse on those as outputs and you walk the tree to the receiving events at the dock.

Forward — "which finished lots contain that input?" The recall-scope question. Query MATCH_inputEPCClass for lgtin:...LOT-RM-4411, collect transformationIDs, resolve every output lot on every event sharing those IDs. The answer here is three lots — A, B, and C — not "everything produced that week," which is what a plant without transformation records concedes to a regulator. Bounded recall scope is the whole economic argument: over-recall is the cost of not knowing, and it is priced in destroyed sellable product.

The supplier phone tree exists because these queries usually cannot be run: consumption lives in a batch record, production in a different system, and the join in somebody's memory of which kettle ran when. A TransformationEvent is that join, captured at the moment it is true, validated at the door, identified by its own content.

For the FSMA-shaped reader: the transformation Critical Tracking Event's key data elements — input traceability lot codes, output lot codes, quantities, location, date — are precisely the fields this fixture carries. A conformant transformation record is the sortable answer already; the mapping is mechanical.

Run it

The fixture family above ships in the golden corpus inside the package — npx epcis.dev validate any of them, or your own:

npx epcis.dev validate your-transformation-event.json
npx epcis.dev hash your-transformation-event.json

No account, no upload; the verbs run entirely on your machine. The full local toolkit is on run it yourself; how the pins hold the schema still is on the conformance page. The layer above this one — the purchase order and invoice context those output lots ship under — is /business-transactions/; the executive briefing on what genealogy buys upstairs lives at visibility.cloud.

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.