DPP data model
A Digital Product Passport (DPP) is a structured record containing the regulatory, environmental, and performance data for a battery product, as required by EU Battery Regulation 2023/1542. This page describes the data model returned by the Traceable public API.
Lifecycle states
Internally a product moves through the ProductStatus states defined in the platform schema:
| Status | Meaning | GET /api/dpp/{slug} response |
|---|---|---|
Draft | Created, not yet submitted | 404 PRODUCT_NOT_FOUND |
Submitted | Submitted for compliance review | 404 PRODUCT_NOT_FOUND |
Approved | Published (publicly accessible) | 200 OK with the DPP body |
Withdrawn | Manufacturer-initiated withdrawal (reversible) | 410 PRODUCT_WITHDRAWN with reason metadata + manufacturer contact + successor link |
Revoked | Regulator-imposed takedown (ESPR Art. 77 / Battery Reg. Art. 76) | 410 Gone |
Deprecated | Voluntary retirement | 404 |
The public API only serves DPPs in the Approved (published) state with a 200. A Draft or Submitted DPP is not publicly accessible: incomplete data must not be exposed until it has been approved for publication.
When a published DPP needs to be updated, the manufacturer publishes a new version. Each publish writes an immutable snapshot and increments the version counter (dpp_metadata.version).
Other status codes the endpoint can return: 429 RATE_LIMITED, 503 SNAPSHOT_INTEGRITY_FAILURE (the published snapshot could not be verified), and 500 INTERNAL_ERROR. All errors use the shape { "error": { "code": "...", "message": "..." } }.
The slug
The slug is the URL-safe unique identifier for a product's DPP. It is:
- Set by the manufacturer at product creation time
- Constrained to lowercase alphanumeric characters and hyphens (e.g.,
swiftvolt-48v-100ah-ev-pack) - Immutable after the DPP is first published — changing a slug would break all QR codes pointing to it
- Used as the path parameter in all DPP API calls
Slugs are unique across the entire Traceable platform.
Top-level DPP structure
The DPP body is not a fixed, category-uniform schema. It is a configuration-driven object: the platform assembles it per product from the category's display configuration, so which sections and fields are present depends on the product's category and on what the operator has populated. Empty values are dropped, so absent keys are normal.
The top-level keys, as produced by the platform DPP mapper, are grouped below. Structural/section keys use camelCase; the raw formData bag and _config use the platform's snake_case field identifiers.
Identity and product summary
| Key | Type | Notes |
|---|---|---|
productId | string | Internal product identifier |
versionId | string | DPP version rendered as "N.0" (e.g. "1.0") |
productName | string | |
productModel | string | |
brandName | string | |
complianceStatus | string | |
categoryId | string | Category configuration id, e.g. cat_bat_1 (see Battery categories) |
productCategory | string | Human-readable category / configuration name |
industryId / industryName | string | |
productDescription | string | |
productImage | string | Resolved to a time-limited signed URL in the response |
qrCodeImage | string | |
dppId | string | null | |
did / issuer_did | string | null | Decentralised identifiers |
verification_status | string | null |
Structured sections (objects and arrays; contents vary by category)
| Key | Type | Notes |
|---|---|---|
productDetails | object | gtin, serialNumber, manufacturingDate, placeOfManufacture, batchId, plus category-specific keys |
manufacturerInfo | object | name, brand, address, gln, website, contact, … |
complianceAndCertificates | array | Certificate entries |
materialAndCircularity | object | recycledContentPercentage, reparabilityScore, dismantlingInstructionsUrl, materialComposition[] |
technicalSpecifications | object | |
lifecycleAndPerformance | object | Present for categories that configure it (e.g. tyre labelling) |
sustainabilityMetrics | object | |
batteryInformation | object | battery_chemistry, battery_category, battery_status, weight_kg, … |
batteryPerformance | object | Nested voltageSpecs, capacitySpecs, healthMetrics, energySpecs, temperatureRange, … |
performanceMetrics | object | Flat performance metrics |
socialResponsibility | object | |
economicOperators | array | ESPR Art. 4 operators (may be omitted) |
technicalMetadata | object | Interoperability metadata (may be omitted) |
compliance | object | eudr, gpsr, epr sub-sections |
careInstructions | object | Present where configured (e.g. textiles) |
environmentalData | object | carbon_footprint_total, water_consumption, plus sustainability metrics |
productSafety | object | ceDeclarationUrl, riskAssessmentUrl, substancesOfConcern[], warnings, safetyLabels, … |
Summary metrics and scores
| Key | Type | Notes |
|---|---|---|
carbonFootprint | number | undefined | Summary total for hero cards: a single number, not a nested object |
recycledContent | number | undefined | Summary percentage |
reparabilityScore | number | undefined | |
overall_score | number | |
compliance_score / traceability_score / sustainability_score | number |
Metadata and provenance
| Key | Type | Notes |
|---|---|---|
dpp_metadata | object | version (integer), issued_at, valid_until, granularity, serial_number, last_updated |
operator | object | { type } |
history | array | Public, regulatory-relevant lifecycle events |
materialSourcing | array | Consent-filtered traceability records (may be omitted) |
dataFreshness | array | Public-tier freshness metadata for dynamic (BMS-sourced) attributes |
dynamicDataUpdatedAt | string | null | When public dynamic data was last updated; null if none |
_config | object | The category display configuration used to assemble the DPP (internal metadata) |
_calculated_fields | object | Server-computed derived values |
formData | object | The raw field_id → value bag (snake_case keys). For public callers this is access-tier gated: restricted values are stripped before serialisation |
lastUpdatedAt | string | Publish time (see Timestamps) |
lastUpdated | string | Time the response was generated |
Timestamps
Timestamps are ISO 8601 strings in UTC. The DPP body does not use createdAt / publishedAt / updatedAt. The real timestamp-bearing fields are:
| Field | Meaning |
|---|---|
lastUpdatedAt | Publish time of the served version (the snapshot's publish time; falls back to first-approval time, then to now for legacy rows) |
lastUpdated | When this API response was generated |
dpp_metadata.issued_at | When the DPP was issued |
dpp_metadata.valid_until | Validity end, when set |
dpp_metadata.last_updated | Date-time of latest update of the DPP (BatteryPass v1.3 attr #4) |
dynamicDataUpdatedAt | When public-tier dynamic (BMS) data was last updated, or null |
Versioning
Two version representations appear in the response:
versionId: a string of the form"N.0"(e.g."1.0"), used asdpp:passportVersionin the JSON-LD output.dpp_metadata.version: the integer version counter (starts at1, incremented on each publish).
The response also carries the current version in the X-DPP-Version HTTP header.
Historical published versions are retrievable as immutable snapshots at GET /api/dpp/{slug}/v/{version}. The GET /api/dpp/{slug} endpoint always returns the current published version.
Restricted fields
Some data is present in the internal model but is not exposed to anonymous public callers. Rather than a single blanket omission, the platform applies access-tier gating:
- Display sections built from restricted (PoLI / authority-tier) fields render a restriction placeholder instead of the value.
- The raw
formDatakey is preserved for shape compatibility, but its restricted values are stripped for public callers before the response is serialised. - A defense-in-depth scrub runs over the public response to assert no restricted value leaked through.
Elevated access (PoLI / authority) surfaces the restricted values. See the PoLI Endpoints page for the access request workflow.