EPCIS 2.0 capture gateway
Post an EPCIS event.
Get back a validated, hashed, append-only record.
Send an EPCIS 2.0 document. It is checked against GS1's pinned schema, stamped, hashed by CBV 2.0 §8.9 and appended — or refused with the standard's own exception type and a pointer to the field. Translate, validate and hash run here with no key. Capture, query and MCP open with one.
EPCIS records the company. epcis.dev also records who performed the event.
curl -sS https://epcis.dev/validate \
-H 'content-type: application/json' \
--data-binary @receiving-2.0.json
200 application/json
{
"type": "OK",
"valid": true,
"schema": {
"artefact": "epcis-json-schema.json",
"version": "2.0.1",
"sha256": "0f46ff694efffd8d8…0ae210af"
},
"errors": []
}
curl -sS https://epcis.dev/hash \
-H 'content-type: application/json' \
--data-binary @receiving-2.0.json
200 application/json
{
"type": "OK",
"algorithm": "CBV 2.0 §8.9 event hash",
"count": 1,
"hashes": [
"ni:///sha-256;d2f4ce56…ver=CBV2.0"
]
}
curl -sS https://epcis.dev/translate \
-H 'content-type: application/xml' \
--data-binary @receiving-1.2.xml
200 application/json
spine-translation-fidelity: round-trip-clean
spine-translation-lossy-paths: 0
{
"type": "OK",
"document": {
"@context": "https://ref.gs1….jsonld",
"type": "EPCISDocument",
"schemaVersion": "2.0",
"epcisBody": { "eventList": [ … ] }
},
"fidelityReport": { … },
"sourceSchemaVersion": "1.2"
}
A refusal is a result.
There is no error banner. A document that fails comes back as the answer, in the standard's own vocabulary, with a JSON Pointer at the field that caused it.
- 400epcisException:ValidationExceptionmalformed JSON
- 415epcisException:UnsupportedMediaTypeExceptionwrong media type
A malformed body comes back as RFC 7807 problem+json, and every type dereferences to a page at /errors/.
POST /validate
{
"type": "ObjectEvent",
"eventTime": "last Tuesday",
"eventTimeZoneOffset": "+00:00",
…
}
200 application/json
{
"type": "OK",
"valid": false,
"schema": {
"artefact": "epcis-json-schema.json",
"version": "2.0.1",
"sha256": "0f46ff694efffd8d8…0ae210af"
},
"errors": [
{
"path": "/epcisBody/eventL…ventTime",
"keyword": "format",
"message": "must match format…e-time\""
}
]
}
What the door does to every event.
Eight things happen to every event you post, and the useful way to read them is as eight things the door will not do. They are stated here before you send anything, because a contract you find out about on rejection is not a contract.
-
01
Validated on every capture.
Each event is machine-checked against the official GS1 EPCIS 2.0.1 JSON schema before anything else happens. The schema is pinned by digest, and every verdict names the digest that judged it.
-
02
Refused in the standard's own words.
A capture that does not validate comes back as RFC 7807 problem+json carrying the standard's own exception type and a JSON Pointer at the field that caused it. Never a vendor error string.
-
03
Stamped by the gateway, not the sender.
recordTime, capturedBy and the attestation grade are applied at the door. Whatever you sent in those fields is stripped. The scriber is not the part being scribed.
-
04
Two grains, never collapsed.
who is the attested observer — a human, an agent, or an embodied agent — asserted by the caller and graded by the door. capturedBy is the warrantor: the account whose key opened the door. Never the same thing, never merged.
-
05
Identified by the standard's own hash.
The EPCIS event hash from CBV 2.0 §8.9, with GS1 Digital Link normalization, checked against the pinned reference vectors — so the same event captured twice is the same event, and a changed event is a different one.
-
06
Append-only, by construction.
No service identity anywhere holds an UPDATE or DELETE grant; the write path has exactly one writer. You can add an event. You cannot un-write one. Corrections are declared events, never edits.
-
07
Minimally scoped on read.
You see your own scope. What is outside it is absent — not grayed out and not redacted. There is no shape left behind to argue about.
-
08
Conforms to EPCIS 2.0 and CBV 2.0.
Implemented against GS1's official specifications, pinned by digest. The spine speaks What/Who/When/Where/Why/How natively; EPCIS 2.0 is its projection, and the projection always validates.
Everyone in this market has built a network that is authoritative because you joined it. The spine makes a record that is verifiable whether or not you joined anything — and it carries, in the record itself, an attested answer to who observed the event, including when the observer was an agent.
The second Who.
EPCIS records the company: its Who is a GLN, and that is correct. A company has no hands, so epcis.dev also records the performer beneath it, named by id.org.ai: the human, the agent, or the robot that took the action.
- capturedByThe warrantor.The key that posted. Stamped by the gateway.
- whoThe observer.Asserted by you, graded by the door.
POST /validate
// asserted by the caller, graded
"spine:who": "did:agent:router-7",
// stamped: the key that posted
"spine:capturedBy": "acct:acme-3pl",
// the door's grade of that assertion
"spine:attestationGrade": "claimed"
200 application/json
{"valid": true, "errors": []}
Nine scenarios, in the grain.
Authored runs of the event model — a cold chain watched across an ocean, a co-manufacturer's line, a cross-dock exception — as the log a capture produces.
Scenarios
scenario 01 · Cold chain, watched across an ocean
- commissioning · sscc:…5501 · 2–8°C · who: human/line-lead · capturedBy: account/creamery
- sensorReport ×3 · temp in_range · who: code/logger ·id.org.ai · capturedBy: account/carrier
- receiving · sensor: in_range · who: human/dock-4 · capturedBy: account/distributor
Capture, the write half.
POST an EPCIS 2.0 document with a capture key. The gateway validates it against GS1's schema, strips the fields it is going to stamp, stamps them, computes the event hash, and appends. A capture job resolves accepted or rejected, never partial.
curl -sS https://api.epcis.dev/capture \
-H 'authorization: Bearer $EPCIS_KEY' \
-H 'content-type: application/json' \
--data-binary @receiving-2.0.json
202 Accepted
location: /capture/{captureID}
One door for people and for agents.
An agent gets the same door as a person: the same interface, the same key, over MCP.
| tool | what it does | readOnlyHint |
|---|---|---|
| capture | POST an EPCIS document through the gateway laws: strip validate project-check stamp append | false |
| query | SimpleEventQuery over whatever was captured | true |
| get_event | one event, by its CBV §8.9 hash identity | true |
| translate | EPCIS 1.1 / 1.2 / 2.0 XML 2.0 JSON-LD, with a per-job fidelity report | true |
Who this is for.
-
Software vendors
Every engineer spent tracking schema and CBV revisions is a feature not shipped. Run your decade of 1.1 and 1.2 XML through the translation door, no key.
Translate -
Integrators
The record layer as a package you can install, read, vendor, and put in a bid. MIT-licensed, no runtime dependencies.
Docs -
Platform teams
Pinned EPCIS 2.0.1 with sha256 provenance, RFC 7807 bodies, the append-only law: the architecture review, answered before it is asked.
Conformance -
Agents
Typed verbs, complete descriptions, deterministic answers, readOnlyHint set where true. Same door, same key, over MCP.
The agent door
Where it stops.
- payload6 MiB per document.6,291,456 bytes; the refusal states the figure.
- authNo key for translate, validate and hash.A capture key opens capture, query and MCP.
- rate limitNone published.When one is set it is printed here and in /openapi.json before it is enforced.
- retentionThe calculators store nothing.What the site logs is a page of its own: what we log.
Get started.
Translate, validate and hash run here with no key. Capture, query and MCP open with a capture key.