The resolve endpoint — public DPP API
This page documents the public DPP endpoint, which is implementation-compliant with GS1 Digital Link Resolver specification v1.4 and the CEN JTC 24 EN 18216 §5 content negotiation rules.
Canonical URL pattern
https://{slug}.dpp.dppagent.com/{primary-scheme}/{primary-id}[/{secondary-key}/{secondary-id}]
{slug} is the brand's tenant slug. Custom-tier brands replace
{slug}.dpp.dppagent.com with a brand-owned domain (e.g.
dpp.example.com) via DNS CNAME — the resolver is the same.
Supported {primary-scheme} values:
| Scheme | Form | Use case |
|---|---|---|
01 |
GS1 GTIN-14 | Apparel, footwear, electronics, food, cosmetics |
8004 |
GS1 GIAI | Reusable assets, durable goods |
vin |
ISO 3779 VIN | Vehicles |
c |
Container code | Containers, packaging |
battery |
EU Battery Reg | Batteries |
products |
Brand-internal | Catch-all, legacy URLs |
Granularity secondary keys (when applicable):
/10/{batch}— batch granularity/21/{serial}— item granularity/22/{cpv}— consumer product variant
Examples:
https://twisttango.dpp.dppagent.com/01/07332485858214
https://twisttango.dpp.dppagent.com/01/07332485858214/10/AW26-001
https://twisttango.dpp.dppagent.com/01/07332485858214/10/AW26-001/21/SR-00451
Content negotiation
Accept header drives the representation:
text/html — branded DPP
The default for browsers. Returns the tenant's themed DPP
page, rendered with the active template
(renderTextileHTML / renderMinimalTechHTML / etc.). Headers:
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Content-Language: sv-SE
Vary: Accept-Language
Cache-Control: public, max-age=3600, s-maxage=86400, stale-while-revalidate=604800
X-Robots-Tag: noindex, nofollow, noarchive (unless tenant opts in)
application/ld+json — JSON-LD
GET /01/07332485858214 HTTP/2
Accept: application/ld+json
{
"@context": [
"https://schema.org",
"https://ref.gs1.org/voc/dpp/"
],
"@type": "Product",
"identifier": "07332485858214",
"gtin14": "07332485858214",
"name": "Pro Winter Jacket",
"brand": { "@type": "Brand", "name": "Example" },
"additionalProperty": [
{ "@type": "PropertyValue", "name": "espr.mat.fibre_composition", "value": "57% Wool, 43% Recycled Polyester" },
...
],
"isPartOf": {
"@type": "ProductGroup",
"identifier": "PW-101"
}
}
Headers identical to the HTML response with Content-Type swapped.
Used by structured-data crawlers and by partner systems doing
catalogue federation.
application/json — compact DPP
{
"passport_id": "https://twisttango.dpp.dppagent.com/01/07332485858214",
"gtin": "07332485858214",
"status": "active",
"granularity": "model",
"category": "textile",
"last_updated": "2026-06-11T07:54:32Z",
"dpp_data": {
"espr.pid.product_name": { "en-GB": "Pro Winter Jacket", "sv-SE": "Vinterjacka Pro" },
"espr.mat.fibre_composition": "57% Wool, 43% Recycled Polyester",
...
}
}
Used by brand-owned client applications. Lightweight and language-aware.
linkType catalogue — GS1 Digital Link §6
?linkType=all returns the catalogue of every applicable resource
for this DPP:
GET /01/07332485858214?linkType=all HTTP/2
Accept: application/json
{
"gtin": "07332485858214",
"linkset": [
{
"linkType": "gs1:digitalProductPassport",
"href": "https://twisttango.dpp.dppagent.com/01/07332485858214",
"title": "Digital Product Passport",
"type": "text/html",
"default": true
},
{
"linkType": "gs1:productInstructions",
"href": "https://twisttango.dpp.dppagent.com/01/07332485858214#sect-07",
"title": "Care instructions",
"anchor": "care"
},
{
"linkType": "gs1:hasSafetyDataSheet",
"href": "https://brand.example.com/sds/PW-101.pdf",
"title": "Safety data sheet"
},
{
"linkType": "gs1:hasCertificationInfo",
"href": "https://twisttango.dpp.dppagent.com/01/07332485858214#sect-09",
"title": "Certifications"
}
]
}
Individual linkTypes can also be requested directly with a 303 redirect (the GS1 spec's recommended pattern):
GET /01/07332485858214?linkType=gs1:productInstructions HTTP/2
HTTP/2 303 See Other
Location: https://twisttango.dpp.dppagent.com/01/07332485858214#sect-07
Default behaviour (no linkType, or linkType=gs1:digitalProductPassport):
serve the DPP itself. gs1:productSustainabilityInfo is accepted
as an alias for backwards compatibility with scanners written against
the pre-2025 GS1 Web Vocabulary.
Lifecycle responses
dpp_status |
HTTP | Body |
|---|---|---|
draft |
404 (JSON) / 200 (HTML preview with watermark) | Drafts are not publicly resolvable; HTML clients see a preview to support on-shelf testing |
active |
200 | Standard DPP |
replaced |
200 | Passport, plus Link header pointing to successor |
withdrawn |
410 | Branded HTML "withdrawn" page (or JSON for API) |
archived |
410 | Branded HTML "archived" page |
recalled |
410 | Recall notice with manufacturer recall-flow link |
The HTML representation for withdrawn / archived / recalled is on-brand (uses the tenant's theme) so the consumer-facing language is recognisable even when the DPP is no longer being served.
Caching
We cache aggressively at the edge:
- Active DPPs —
Cache-Control: public, max-age=3600, s-maxage=86400, stale-while-revalidate=604800. Edge holds the response for 24h; clients refresh hourly. - Draft / preview —
Cache-Control: no-store, max-age=0. - 404 / 410 — short cache (5 min) to absorb scan storms.
The CDN respects Vary: Accept-Language so per-locale variants
cache independently.
When a brand publishes a revision the resolver issues a targeted purge against the canonical URL. There's no global TTL flush; only the affected DPP is invalidated.
Rate limits
None applied to public DPP reads. The endpoint is designed to be scannable from QR codes on consumer-facing packaging where sudden traffic spikes (campaign launches, retail events) are expected.
Admin and integration endpoints are rate-limited and require authentication.
Query parameters
| Param | Values | Effect |
|---|---|---|
linkType |
gs1:pip, gs1:tds, all (+ more) |
GS1 Digital Link §6 link-type selection. all returns a directory JSON of every linkType the brand has registered for this identifier. |
schema |
cirpass-2 / cirpass |
When the Accept header asks for JSON, returns the nested CIRPASS-2 object instead of the default ESPR-flat key namespace. |
as |
role-slug (consumer, manufacturer, ...) | Section-audience impersonation. Picks which DPP sections the resolver renders. Defaults to consumer when omitted; unknown slugs fall back to consumer. Has no effect for tenants with role_access_enabled = FALSE (the platform default). See Security & privacy → Roles. |
preview |
1 |
Render non-public DPP statuses (draft, withdrawn, archived, recalled) with a watermark. Operators only — the link is suggested in the admin UI but the resolver does not require auth (the URLs are unguessable). |
?as= whitelist:
consumer · economic-operator · manufacturer · importer · distributor ·
online-marketplace · repairer · refurbisher · recycler · customs-authority ·
market-surveillance · competent-authority
Plus any per-tenant custom roles registered in the role catalogue.
Open-graph and social sharing
The HTML response includes complete OG tags:
<meta property="og:type" content="product">
<meta property="og:title" content="Pro Winter Jacket — Example">
<meta property="og:description" content="Digital Product Passport: composition, care, recycling.">
<meta property="og:image" content="https://cdn.example.com/products/PW-101-hero.jpg">
<meta property="og:url" content="https://twisttango.dpp.dppagent.com/01/07332485858214">
The image falls back to the hero image, then the brand logo, then a
generic placeholder — see the hero image resolution rules in
api/v1/resolve.js for the exact walk order.
Error responses
All errors are JSON unless the client sent Accept: text/html,
in which case they render as a branded notice page.
{
"error": "Primary identifier missing from GS1 GTIN path",
"path": "/01/",
"scheme": "gtin"
}
| Code | Reason |
|---|---|
| 400 | Malformed path or unknown scheme |
| 404 | Path well-formed but no DPP matches |
| 410 | DPP withdrawn / archived / recalled |
| 503 | Backing store temporarily unreachable |