dpp-sdk
dpp-sdk is a Python library for Digital Product Passports (DPPs). With it you can:
- describe a product passport in Python and check that it is valid,
- turn it into JSON and back,
- save it in a DPP repository and register it in a DPP registry.
The clients work with any repository or registry that implements the EN 18222 API. For local testing, you can start example Java services with one command (see below).
Install
You need Python 3.11 or newer.
pip install dpp-sdk
Create, save, and register a passport
1. Start a repository and a registry
Skip this step if you already have EN 18222 services. Otherwise, start the example Java services locally. This needs Docker (on Windows and macOS: Docker Desktop, started).
Download the service definition into an empty folder and start it:
Windows (PowerShell):
curl.exe -O https://raw.githubusercontent.com/CIR4FUN-EU/python-dpp-sdk/main/examples/mock-services-demo/compose.yaml
docker compose up -d --wait
Linux/macOS:
curl -O https://raw.githubusercontent.com/CIR4FUN-EU/python-dpp-sdk/main/examples/mock-services-demo/compose.yaml
docker compose up -d --wait
When the command finishes, the repository runs at http://localhost:8080 and the registry at
http://localhost:8081.
2. Run the workflow
Save this as workflow.py in the same folder:
import json
from datetime import UTC, datetime
from uuid import uuid4
from dpp_sdk import Dpp4FunJsonCodec, DppHeader, Granularity, validate_dpp4fun
from dpp_sdk.clients import (
DppRegistryClient,
DppRepoClient,
DppRepresentation,
RegisterDppRequest,
)
from dpp_sdk.dpp4fun import ChairCharacteristics, Dpp4Fun, Nameplate
# Replace these with your own services. Any EN 18222 repository/registry works.
REPO_URL = "http://localhost:8080"
REGISTRY_URL = "http://localhost:8081"
# 1. Create a passport for a blue chair.
token = uuid4().hex # unique IDs, so you can run this script more than once
passport = Dpp4Fun.create(
DppHeader(
digital_product_passport_id=f"urn:example:dpp:{token}",
unique_product_identifier=f"urn:example:product:{token}",
granularity=Granularity.ITEM,
last_updated=datetime.now(UTC),
dpp_status="active",
economic_operator_id="urn:example:operator:demo",
),
Nameplate.create(manufacturerName="Example Furniture"),
ChairCharacteristics.create(color="blue"),
)
# 2. Check it before sending it anywhere.
report = validate_dpp4fun(passport)
if not report.is_valid:
raise SystemExit(f"Passport is not valid: {report.issues}")
# 3. Save it in the repository.
full_json = json.loads(Dpp4FunJsonCodec().to_json(passport))
with DppRepoClient(REPO_URL) as repository:
created = repository.create_dpp(full_json, DppRepresentation.FULL)
passport_id = created.digital_product_passport_id or passport.header.digital_product_passport_id
print("Saved passport:", passport_id)
# 4. Register it, so others can find it.
with DppRegistryClient(REGISTRY_URL) as registry:
registration = registry.post_new_dpp_to_registry(
RegisterDppRequest(
unique_product_identifier=passport.header.unique_product_identifier,
digital_product_passport_id=passport_id,
unique_economic_operator_identifier=passport.header.economic_operator_id,
dpp_api_endpoint=f"{REPO_URL}/v1/dpps",
)
)
print("Registered:", registration.registration_id)
# 5. Read it back as a typed passport.
with DppRepoClient(REPO_URL) as repository:
response = repository.read_dpp_by_id(passport_id, DppRepresentation.FULL)
loaded = Dpp4FunJsonCodec().from_json(json.dumps(response.payload))
print("Manufacturer:", loaded.typed_aspect(Nameplate).field("manufacturerName").value)
Run it:
python workflow.py
You should see three lines similar to:
Saved passport: urn:example:dpp:a06675bc10d4439e9b6de17e899c51b1
Registered: e532b61a-d1cb-47dd-8589-ce6a2f387de8
Manufacturer: Example Furniture
The clients send and return plain JSON (Python dicts), and the codecs read and write JSON strings.
That is why the script uses json.loads() before sending and json.dumps() after reading.
Steps 1 and 2 of the script (create and check) work without any service.
To also read, update, look up older versions of, and delete a passport, see the full lifecycle example.
3. Stop the services
Run this in the same folder:
docker compose down
The stored data is kept for next time. To delete it too, run docker compose down -v.
How the SDK is organized
| Part | What it does |
|---|---|
dpp_sdk.core |
General passport model, validation, and JSON conversion. |
dpp_sdk.dpp4fun |
Ready-made furniture passport (chairs, sofas, …) built on Core. |
dpp_sdk.clients |
HTTP clients for repositories and registries. They send and receive plain JSON and don't depend on the models. |
The DPP4Fun furniture passport
dpp_sdk.dpp4fun is a ready-to-use passport for furniture, based on the DPP4Fun profile (D4.1).
You fill it with typed Python objects instead of building raw DataElements yourself.
A passport has a header plus any of these 19 aspects:
| Topic | Aspects |
|---|---|
| Identity and origin | Nameplate, Traceability, ProductRelationship |
| Environment and health | EnvironmentalImpact, CarbonFootprint, BillOfMaterials, HealthImpact |
| Circularity | Durability, Reliability, Repairability, Refurbishment, Remanufacturing, Reusability, Recyclability, Upgradability, WasteManagement |
| Use and rights | Usage, Safety, RightOfEndUsers |
It can also have one product type with its own fields: ChairCharacteristics,
CribCharacteristics, FabricCharacteristics, LockerCharacteristics,
ParticleBoardCharacteristics, or SofaCharacteristics.
Reusable building blocks go inside the aspects, for example Material, Component, Part,
Certificate, Instruction, Contact, and records such as ManufacturingRecord or RepairRecord.
What you can do with it:
- Create and edit passports with typed fields. Edits return a new copy (
with_field()). - Use units safely:
Dpp4FunQuantitypairs a number with aLengthUnit,MassUnit, orVolumeUnit. Values with the wrong kind of unit are rejected. - Validate against the D4.1 rules with
validate_dpp4fun(). You get a list of issues, each with the path of the field it concerns. - Convert to and from FULL and COMPRESSED JSON with
Dpp4FunJsonCodec. The built-in profile describes all 488 fields, so COMPRESSED JSON can be expanded again. - Keep extra data: fields that are not part of the profile are kept, not dropped.
Field names are the camelCase IDs used in JSON (manufacturerName=, not manufacturer_name=).
The field reference
lists all of them per aspect, and the model guide
shows how to build, read, edit, and validate passports.
Handle errors
Validation never raises; it returns a report (report.is_valid, report.issues). Everything else
raises a specific exception:
| Exception | Raised when |
|---|---|
DppNetworkClientError |
The service cannot be reached, or the request times out. |
DppApiClientError |
The service answers with an API error, for example ClientErrorResourceNotFound. Has the HTTP status and the service messages. |
DppHttpClientError |
The service answers with an HTTP error that is not an API error envelope. |
DppMappingClientError |
A request cannot be serialized, or a response is not a valid envelope. |
DppMappingError |
Passport JSON cannot be read or written (bad JSON, wrong structure, missing header fields). |
DppValidationError |
Only from from_json_and_validate(): the JSON was read, but the passport is not valid. |
The client exceptions are in dpp_sdk.clients; the model exceptions are in dpp_sdk.
from dpp_sdk.clients import DppApiClientError, DppNetworkClientError
try:
with DppRepoClient(REPO_URL) as repository:
repository.read_dpp_by_id(passport_id)
except DppApiClientError as error:
print("Service returned an error:", error)
except DppNetworkClientError:
print("Service not reachable")
Documentation
| If you want to… | Read |
|---|---|
| build, read, edit, validate, and convert passports | Model guide |
| find the field names of a DPP4Fun aspect | DPP4Fun field reference |
| see every client method and error | HTTP clients guide |
| build passports for another product group | Building your own profile |
| trace behavior to EN 18222/18223/18219 or D4.1 | Standards alignment and validation rules |
| run the guided demo with test scenarios | Mock-services demo |
| see what changed, or upgrade from an older version | Changelog |
| contribute, run the tests, or work on this repository | Contributing guide |
Terms used here
- DPP (Digital Product Passport): a structured record about one product, model, or batch.
- Repository: a service that stores passports.
- Registry: a service that records where a passport is stored, so it can be found.
- FULL / COMPRESSED: two JSON forms of a passport. FULL includes type information for every field; COMPRESSED is shorter and contains only the values.
- DataElement: one field or group of fields inside a passport.
- DPP4Fun: a furniture passport profile, defined in the project document D4.1.
- AAS (Asset Administration Shell): the template format in which the DPP4Fun fields are published.
Standards and limitations
The SDK follows selected parts of these standards. This is not a claim of full conformance, certification, or legal compliance.
- EN 18222:2026 (repository and registry API): Clauses 4.2–4.8, 5.2, 6.2–6.3, 7.2, 8.1–8.4.
- EN 18223:2026 (passport model and JSON): Clauses 4.1.2.1–4.1.2.9 and 5.2.2–5.2.9.
- EN 18219:2026 (identifiers): local syntax checks for GS1 Digital Link, GS1 GLN, and Identification Link only. The SDK does not issue, resolve, or prove uniqueness of identifiers.
- DPP4Fun D4.1 v1.0, Sections 3.2.1–3.2.2. Where D4.1 contradicts itself, the standards page explains which choice the SDK makes.
Not included:
- No authentication, retries, caching, or automatic pagination (you pass
cursoryourself). - No complete JSON Merge Patch (RFC 7396) or JSONPath (RFC 9535) implementation. The client sends your update as-is, and the service decides how to apply it.
- No registry delete or read-back, no backup-operator support, and no lifecycle-event history.
- COMPRESSED JSON is the JSON API shape used by the Java services, not the XML form in EN 18223 Annex B.
- No connection to a real EU registry, and no production hardening.
Versioning
dpp-sdk is in the 0.x phase: a new minor version may contain breaking changes. Every
breaking change is listed in the changelog with migration notes. The public API
is what dpp_sdk, dpp_sdk.core, dpp_sdk.dpp4fun, and dpp_sdk.clients export; modules and
names starting with _ are internal.
Contributing and license
To work on the SDK itself, see the contributing guide. The release steps are in the release guide. The license is in LICENSE.
Metadata
Release files for dpp-sdk 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dpp_sdk-0.5.0.tar.gz | 162.8 kB | With provenance | Details for dpp_sdk-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dpp_sdk-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for dpp_sdk-0.5.0-py3-none-any.whl |
Total release size: 271.4 kB
Release files / dpp_sdk-0.5.0.tar.gz
| Download URL | dpp_sdk-0.5.0.tar.gz |
|---|---|
| Size | 162.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
13e81c81e4e29e4862772bf6c28d1f824f8bb1f9fca5346172c6c4722a426c05
|
|
BLAKE2b-256 checksum How to use checksums |
7633310a883c9ad04e894394f8561f70f1e271374024775fb228f1936bbc294e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logRelease files / dpp_sdk-0.5.0-py3-none-any.whl
| Download URL | dpp_sdk-0.5.0-py3-none-any.whl |
|---|---|
| Size | 108.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0b691fb3395a7cb8d416004b6564ab36d6188a960f6f9515d37ef937a18f446d
|
|
BLAKE2b-256 checksum How to use checksums |
b8a5f34d69975da331c3824ac023b64f68519bcfec80b10668dc30aae1c98338
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logSecurity for dpp-sdk 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for dpp_sdk-0.5.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.