The EU has not finalised one canonical DPP schema. What exists in May 2026 is a layered stack: the CIRPASS-2 Core Ontology proposal from April 2025 at the top, GS1 Digital Link for the URL form, Schema.org as the base vocabulary, and a small handful of custom DPP fields that the official CEN/CENELEC EN 1821x standards are expected to formalise later this year.
For anyone implementing today, the pragmatic position is to build against this layered stack and treat the custom namespace as a swap target. When EN 1821x publishes, the field names you used probably stay; the namespace URI gets replaced.
Below is what every field looks like in practice.
What does a DPP JSON-LD record look like?
Here is a stripped-down example of a real DPP record. The product is one numbered edition of a hypothetical leather wallet from a craft maker in Florence:
// dpp.jsonld for editions 7 of 50, "Walter wallet"
{
"@context": [
"https://schema.org",
{ "gs1": "https://gs1.org/voc/" },
{ "dpp": "https://editioned.app/vocab/dpp#" }
],
"@type": "Product",
"@id": "https://atelier.com/products/walter-wallet?edition=7&token=abcd1234",
"identifier": {
"@type": "PropertyValue",
"propertyID": "editioned:token",
"value": "abcd1234efgh5678ijkl9012"
},
"name": "Walter wallet",
"manufacturer": {
"@type": "Organization",
"name": "Atelier Bottega Walter",
"address": "Via dei Saponai 12, Firenze, IT",
"addressCountry": "IT",
"dpp:euOperator": "Atelier Bottega Walter S.r.l."
},
"material": [
{ "@type": "PropertyValue", "name": "Vegetable-tanned leather", "value": 88, "unitCode": "P1" },
{ "@type": "PropertyValue", "name": "Brass hardware", "value": 12, "unitCode": "P1" }
],
"dpp:editionNumber": 7,
"dpp:editionTotal": 50,
"dpp:dateManufactured": "2026-03-14",
"dpp:carbonFootprint": { "@type": "QuantitativeValue", "value": 2.4, "unitCode": "KGM" },
"dpp:recycledContentPercent": 0,
"dpp:expectedLifespan": { "@type": "QuantitativeValue", "value": 40, "unitCode": "ANN" },
"dpp:repairInstructions": { "@type": "URL", "url": "https://atelier.com/care" },
"dpp:conformityMarkings": ["REACH"]
}
Roughly 300 bytes of payload, fully machine-readable, no third-party dependencies to fetch. That is the entire record for one piece. Every field has a purpose.
Where do DPP field names come from? (@context explained)
JSON-LD's @context tells parsers how to interpret each key. A DPP record uses three sources:
- Schema.org for the universal product / organisation / measurement types.
Product,Organization,PropertyValue,QuantitativeValueall come from here. These exist in every Schema.org-aware tool, including Google Rich Results, validators, and most enterprise PIM systems. - GS1 vocabulary for trade-item identifiers.
gs1:gtin,gs1:additionalTradeItemIdentification, and the rest of the GS1 namespace become relevant the moment a product has a real Global Trade Item Number. For craft goods without GTINs, this section stays empty and the identifier carries a different propertyID. - The custom
dpp:namespace. Fields that exist nowhere else:dpp:carbonFootprint,dpp:euOperator,dpp:expectedLifespan,dpp:editionNumber, and so on. The URI we use is a placeholder, see the fulldpp:vocabulary reference for every property and the CEN/CENELEC swap plan. When CEN/CENELEC EN 1821x publishes (expected mid-2026), the field names will probably stay roughly the same and the namespace URI will be the official one. Swapping is a search-and-replace.
dpp: JSON-LD context, JSON Schema validator, and five sector examples are published as an open specification under CC0. Install via npm install @editioned/dpp-schema, browse the source on GitHub, or read the property reference at /vocab/dpp.
What goes in the DPP identifier block?
DPP requires a unique per-unit identifier. For batteries this will be a battery passport number plus the manufacturer's serial. For textiles and electronics under future delegated acts, it will most likely be GTIN plus serial. For craft goods without GTIN, you use whatever globally unique token you have. UUIDs work fine.
Two patterns show up most often:
// Pattern A: token-based (Editioned's default)
"identifier": {
"@type": "PropertyValue",
"propertyID": "editioned:token",
"value": "abcd1234efgh5678ijkl9012"
}
// Pattern B: GS1 GTIN + serial
"identifier": [
{ "@type": "PropertyValue", "propertyID": "gs1:gtin", "value": "01234567890128" },
{ "@type": "PropertyValue", "propertyID": "gs1:serialNumber", "value": "SN-2026-00347" }
]
Whichever you use, the rule that matters: the value must identify this specific physical piece, not the SKU. A SKU-only identifier is a per-product passport, which is not what DPP requires. Get this wrong on day one and retrofitting unique IDs across thousands of pieces is painful.
How do you encode manufacturer and EU operator data?
Two often-confused roles. The manufacturer is who physically produced the goods. The EU operator is the legal entity placing the product on the EU market. For an EU-based atelier selling direct to EU buyers, both are the same company. For a non-EU craft maker selling through an EU importer, they are different.
The EU operator is the entity legally responsible for the DPP's correctness. They are who customs and market surveillance authorities call when something is wrong. If you ship from outside the EU directly to an EU customer with no importer in between, you become the EU operator yourself (or you appoint an authorised EU representative).
This block looks small in the JSON, but legally it is the most important field on the page. Get the name wrong and the passport is invalid even if every other field is right.
"manufacturer": {
"@type": "Organization",
"name": "Atelier Bottega Walter",
"address": "Via dei Saponai 12, Firenze, IT",
"addressCountry": "IT",
"dpp:euOperator": "Atelier Bottega Walter S.r.l."
}
How do you structure material composition?
For textiles this becomes the most demanding section. For batteries it is dominated by cobalt, lithium, nickel, and lead. For craft goods it is often a small list of two or three primary materials plus hardware.
The convention is one PropertyValue per material, with the name in name and the percentage in value, plus the UN/CEFACT unit code P1 meaning "percent". The list should sum to 100 (or close to it; the spec does not currently require a hard sum-check).
"material": [
{ "@type": "PropertyValue", "name": "Vegetable-tanned leather", "value": 88, "unitCode": "P1" },
{ "@type": "PropertyValue", "name": "Brass hardware", "value": 12, "unitCode": "P1" }
]
For batteries, replace P1 with mass-based codes (typically KGM for kilograms) where the regulation asks for cobalt content in absolute terms rather than percentages. The Battery Pass Consortium has the canonical reference for which fields are percentage-based and which are mass-based.
If your product contains any Substance of Very High Concern (SVHC) declared under REACH above 0.1% by weight, declare it in a separate field:
"dpp:reachSvhc": "Cobalt dichloride 0.15%"
"None" is a valid value when your product contains nothing on the SVHC candidate list. Skipping the field entirely is technically allowed but reviewers prefer an explicit declaration.
What sustainability fields does the DPP require?
Four fields that recur across every sector's delegated act in some form:
- Carbon footprint in kg CO₂-equivalent, declared per unit (not per kg of material). Most craft producers will not have this number on hand; the delegated acts for textiles and electronics provide simplified default methodologies for small manufacturers who cannot afford a full LCA.
- Energy consumption in kWh, relevant for electronics only.
- Recycled content as a percentage, by material. For batteries this is granular (cobalt, lithium, nickel, lead each declared separately).
- Recyclability score as a 0 to 100 integer. For most consumer goods this is computed against a category-specific rubric that the delegated act provides.
Schema.org's QuantitativeValue structure with explicit unitCode is what every parser expects:
"dpp:carbonFootprint": {
"@type": "QuantitativeValue",
"value": 2.4,
"unitCode": "KGM"
}
KGM is UN/CEFACT code for kilogram. KWH for kilowatt-hour. ANN for years. P1 for percent. The codes are tedious but they are the universal standard, and using them avoids ambiguity between (for example) "kg" and "lb".
How do you encode lifecycle and circularity data?
This block exists to give downstream actors (repairers, recyclers, refurbishers) the data they need to extend the product's useful life. Four fields cover most of it:
- Expected lifespan in years. For batteries the regulation uses cycle count instead. For furniture it is a usage-based number rather than calendar years.
- Repair instructions URL. Can be a PDF, a video, a structured BOM page. The instruction must be accessible to independent repairers (not locked behind a manufacturer-only login). This is part of the EU's Right to Repair framework.
- Recycling instructions URL. Same shape, end-of-life-focused.
- Spare parts availability. Free-text description of what's available, for how long, at what price.
For craft goods these often resolve to a single page on the maker's site that explains care, repair, and end-of-life options together.
How do you record compliance markings (CE, GS1, etc.)?
The dpp:conformityMarkings field is a string array. Each value is a marking the product carries:
"dpp:conformityMarkings": ["CE", "REACH", "RoHS"]
Six markings cover almost every consumer product:
- CE: general EU conformity
- UKCA: UK Conformity Assessed (post-Brexit equivalent to CE)
- FCC: US radio / electronic equipment
- RoHS: restriction of hazardous substances in electronics
- REACH: chemicals registration compliance
- WEEE: waste electrical / electronic equipment
Use the marking abbreviation as the string. Tools that validate DPP records have an enum of these and will warn on unknowns.
Which validators check DPP JSON-LD records?
As of May 2026 there is no single official EU DPP validator. The Commission is funding one as part of CIRPASS-2, but it is still in beta. In practice three tools cover the validation needs of most teams:
Schema.org validator
The most widely used JSON-LD validator on the web. It will not check DPP-specific fields (everything under the dpp: namespace is opaque to it), but it catches structural errors: missing @type, wrong type for a known property, malformed @context. Run every DPP record through it first.
Google Rich Results test
The same Schema.org validator with extra checks for fields Google extracts into rich snippets. Useful even though Google does not specifically support DPP, because if your record passes Rich Results it almost certainly passes any other Schema.org-based validator.
CIRPASS-2 reference validator (beta)
The CIRPASS project maintains a SHACL-based validator that checks the custom DPP fields against the Core Ontology shapes. As of May 2026 it requires registration and is not stable enough for CI use. Expect a public stable version around the same time as EN 1821x publishes.
The Battery Pass Consortium validator
For battery products only. The Battery Pass Consortium (industry group) maintains a sector-specific validator that checks Annex XIII compliance. If you are shipping any product that contains a battery over 2 kWh, this is the one validator you actually have to pass before Feb 2027.
What are the most common DPP JSON-LD errors?
From working with DPP records day-to-day, four mistakes show up most often:
One. Using SKU as identifier instead of per-unit ID. The validator does not catch this; the regulation does. Every DPP must identify a specific physical piece. If you cannot link the JSON-LD to a single unit, the passport is invalid even if every other field passes.
Two. Mixed units inside the materials block. Some materials in percent, others in grams. The unit codes prevent this in theory but parsers handle it inconsistently. Pick one unit system per record and stick to it.
Three. Missing addressCountry on the manufacturer. Schema.org's Organization has address as a free-text field, which is fine for display but does not let customs systems parse the country. Always include addressCountry as a separate ISO 3166 alpha-2 code.
Four. Wrong unit code on QuantitativeValue. KG is not a UN/CEFACT code; the correct value is KGM. YR is not valid; use ANN. This catches almost everyone the first time.
Editioned exports CIRPASS-aligned JSON-LD.
One click per edition produces the schema described in this post. The merchant downloads the file and submits it to their chosen DPP service or compliance partner; we don't post to the EU registry directly (yet).
See how it works →What's stable in DPP today, and what's still moving?
Three things in this stack are stable enough to build on today:
- Schema.org base types (
Product,Organization,PropertyValue,QuantitativeValue). These have not meaningfully changed in years. - GS1 Digital Link URL format. Stable since 2018, used by hundreds of thousands of products.
- UN/CEFACT unit codes. Slow-moving by design.
Three things will move between now and late 2026:
- The CIRPASS-2 Core Ontology namespace will be replaced by the official EN 1821x URI. Field names will probably stay close but expect minor renames.
- Per-sector delegated acts will add sector-specific required fields. Textiles will add fibre composition. Electronics will add energy classification.
- The official CEN/CENELEC DPP validator will replace the CIRPASS-2 beta. Expect stricter SHACL shapes.
Build today against the stable layer. Treat the custom namespace as a swap target. When the delegated act for your sector arrives, you will have weeks of incremental work, not months of rebuilding.
Sources
How Editioned builds this record
Editioned exports a CIRPASS-aligned Digital Product Passport record per edition, alongside numbered editions and certificates of authenticity for Shopify.
See the DPP export