Skip to content

UNTP and EN 18222 interop

No region is the universal envelope. The gateway renders the neutral core in foreign protocol shapes — a protocol is a render profile, not a fork — and imports them back with deterministic identity. Adding a protocol adds a render profile; the core never grows a region’s fields.

GET /untp/product/{id}?freshness= returns the verifiable-credential triad:

  • the DigitalProductPassport VC,
  • the DigitalConformityCredentials (profile bindings + inspection stamps),
  • a link-resolver entry anchored at the log head,

plus the verdict over the evidence. Recorded against the reference deployment:

Terminal window
$ curl -s -G 'http://127.0.0.1:8395/untp/product/urn%3Aunidpp%3Apassport%3Apilot-e8-j000842' \
| jq '{source: .rendering.source, outcome: .verdict.outcome,
signatures_verified: .verdict.coverage.signatures.verified}'
{
"source": "issuer",
"outcome": "pass",
"signatures_verified": 5
}

The five signatures are the passport’s five lifecycle events, verified against the issuer’s published anchors during the render.

Identifier mapping: core cpid: identifiers map to https://unidpp.org/id/ URIs; the GS1 family maps to https://gs1.org/voc/ with (01)/(10)/(21) application identifiers. The render names its profile (urn:unidpp:profile:render:untp).

POST /untp/ingest accepts a UNTP passport VC (bare or as a triad) and mints a core passport with a deterministic identity — idempotent per subject: submitting the same VC twice yields one core passport, not two. Imported documents carry an empty event log (their events live in the source regime) and a receipt recording the origin. Render and ingest are inverse projections.

GET /en18222/v1/dppsByProductId/{gtin}?representation=full|compressed — the EN 18222 REST shape, default compressed per the EN. Recorded:

Terminal window
$ curl -s 'http://127.0.0.1:8395/en18222/v1/dppsByProductId/4006381333931?representation=full' \
| jq '.dppStatus'
"active"
  • full: the element tree, string-printed values;
  • compressed: collection-keyed, native JSON values.

The wire field set is mirrored from the freeDPP live-endpoint artifacts (the conformance corpus keeps both representations as fixtures) — an EN 18222 client sees what it expects, including the frozen-wire rule: no as_of member on the EN 18222 body (the x-as-of header still carries it).

The renderings are ports, not re-derivations: the UNTP adapter’s semantics come from the Python reference adapters (unidpp-py), and the EN 18222 wire from the freeDPP artifacts. The gateway’s discovery document states both provenances. What that buys an operator: when a question arises — is this field part of the compressed representation? does the triad include the resolver entry? — the answer lives in the reference implementation and the conformance corpus, not in prose.

  • No upstream, or upstream unreachable → fixtures serve, the render metadata names the source (fixtures).
  • Unknown and deliberately unresolvable ids → identical 404 bytes (I12).
  • ?freshness= on the UNTP render bounds the evidence freshness window the verdict applies.

The gateway serves both simultaneously; there is no either/or. A deployment facing EU regulation serves the EN 18222 binding; one facing UNTP counterparts serves the triad; both come from the same core passports, signed by the same keyrings, anchored in the same log. The choice of what to tunnel to the public internet is a per-deployment exposure decision (see security posture).