DPP Resolution: How We Wired It Up

How OpenEPCIS combined GS1 Digital Link, the IETF linkset format and content negotiation into one DPP resolution flow

One way to take a scanned QR code through to a typed DPP response, using GS1 standards. A worked example, not the canonical flow for every regulator.

The standards below are published: GS1 Digital Link, the GS1 Conformant Resolver discovery contract, application/linkset+json (RFC 9264), HTTP content negotiation, ESPR Article 9 access tiers. The choices between them are ours.

The flow at a glance

From data carrier through the GS1 resolver to a tier-gated DPP responseFrom data carrier through the GS1 resolver to a tier-gated DPP response

Walking it through with one example

The example below uses a serialised timber product — chosen because timber under EUDR exercises the access-tier surface (a customs officer needs more detail than a consumer). The same primitives apply when the product is a battery, a garment, a smartphone or a frying pan; only the linkset entries and the JSON-LD payloads change.

The QR code carries a GS1 Digital Link URI:

https://id.gs1.org/01/09521234000020/21/LOG-2025-001
        └────┬───┘ └──────┬─────┘ └──────┬─────┘
          Base       GTIN (AI 01)   Serial (AI 21)

Application Identifiers (01, 21, …) are the same primitives GS1 has used in barcodes for decades. Encoding them in a URL means the same identifier is scannable by a phone camera, queryable by a server, and visible in a browser bar.

2 — Resolver lookup

The resolver matches the GTIN against its registry and returns an application/linkset+json document (IETF RFC 9264). One object per anchor, GS1 link relations as full-IRI keys, link descriptors as values. The discovery contract that lets downstream registries find and validate the deployment is the GS1 Conformant Resolver standard — see Resolver Setup for the full shape and the hosting choices we considered.

3 — Content negotiation

The same Digital Link URL serves different representations depending on the Accept header. A consumer's phone browser sends text/html and lands on a passport web page. A downstream system asks for application/ld+json and gets a typed JSON-LD record. RDF tooling asks for text/turtle. This is plain HTTP content negotiation, not anything GS1-specific:

Accept headerResponse
text/htmlConsumer-facing passport page
application/ld+jsonJSON-LD record
application/jsonPlain JSON
text/turtleRDF / Turtle

4 — linkType for facet selection

Layered on top of Accept, the ?linkType= query parameter requests a specific facet of the passport. Per the GS1 Digital Link spec the value is the bare relation name (the compact gs1:… form only appears as keys inside the linkset JSON itself).

The list below is the relations our resolver auto-derives today; a deployment can extend it with sector-specific relations as needed.

?linkType=What it returns
pipProduct Information Page (default)
epcisEPCIS event history
certificationInfoCertificates and declarations
safetyInfoSafety data sheet
recipeInfoComposition / BOM
serviceInfoService and repair
productSustainabilityInfoCarbon footprint, recycling

5 — Access tier (ESPR Article 9)

ESPR Article 9 defines three access tiers for DPP data. The resolver enforces them at the request boundary:

How a DPP endpoint decides what to return per ESPR access tierHow a DPP endpoint decides what to return per ESPR access tier
TierWho can read itExamples
PublicAnyoneProduct name, manufacturer, recycling instructions, headline specs
AuthorizedOnlyMarket surveillance, customs, accredited recyclersTest reports, compliance dossiers, detailed supply chain
RestrictedNamed partners under agreementTrade secrets, proprietary formulations, full BOM

In our implementation the Authorized and Restricted tiers reuse the same identity layer that gates the EPCIS REST surface — a Keycloak-issued credential, a capability token, or (where we've wired it up) a Verifiable Credential presented via OID4VP. See Architecture → Wallet-agnostic Verifiable Credentials.

Instance URIs vs vocabulary URIs

The URIs that identify a specific product and the URIs that define what a property means are distinct, and confusing them is a common early mistake:

  • Instance URIhttps://id.gs1.org/01/09521234000020/21/LOG-2025-001 resolves to this specific timber log's record.
  • Vocabulary URIhttps://ref.openepcis.org/extensions/eu/eudr/commodityType identifies the commodityType term. The term is documented on its module page, eu/eudr; the term URI itself is an identifier, not a page.

In a JSON-LD document the @id is an instance URI; predicates (battery:stateOfHealth, eudr:commodityType, …) expand to vocabulary URIs through the @context.

Out of scope

A few things this page deliberately doesn't claim:

  • It isn't a finished product offering. We've built the resolution mechanism and exercised it; the per-sector DPP credentials and the regulator-side acceptance criteria still depend on the underlying regulation landing (ESPR delegated acts, EU Central DPP Registry, sector EN standards). Status notes live on the per-domain implementation pages.
  • It isn't the only valid resolution flow. Other implementations put the resolver on a brand-owned domain, redirect rather than serve, gate everything behind authentication, fall back to a static page, or short-circuit content negotiation entirely. Each makes different trade-offs.
  • It isn't sector-specific. The primitives — Digital Link URI, linkset, content negotiation, linkType, access tier — are sector-neutral. The per-sector work is which fields go in the JSON-LD payload, not how the URL gets resolved.

Next steps