Skip to main content

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:

StatusMeaningGET /api/dpp/{slug} response
DraftCreated, not yet submitted404 PRODUCT_NOT_FOUND
SubmittedSubmitted for compliance review404 PRODUCT_NOT_FOUND
ApprovedPublished (publicly accessible)200 OK with the DPP body
WithdrawnManufacturer-initiated withdrawal (reversible)410 PRODUCT_WITHDRAWN with reason metadata + manufacturer contact + successor link
RevokedRegulator-imposed takedown (ESPR Art. 77 / Battery Reg. Art. 76)410 Gone
DeprecatedVoluntary retirement404

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

KeyTypeNotes
productIdstringInternal product identifier
versionIdstringDPP version rendered as "N.0" (e.g. "1.0")
productNamestring
productModelstring
brandNamestring
complianceStatusstring
categoryIdstringCategory configuration id, e.g. cat_bat_1 (see Battery categories)
productCategorystringHuman-readable category / configuration name
industryId / industryNamestring
productDescriptionstring
productImagestringResolved to a time-limited signed URL in the response
qrCodeImagestring
dppIdstring | null
did / issuer_didstring | nullDecentralised identifiers
verification_statusstring | null

Structured sections (objects and arrays; contents vary by category)

KeyTypeNotes
productDetailsobjectgtin, serialNumber, manufacturingDate, placeOfManufacture, batchId, plus category-specific keys
manufacturerInfoobjectname, brand, address, gln, website, contact, …
complianceAndCertificatesarrayCertificate entries
materialAndCircularityobjectrecycledContentPercentage, reparabilityScore, dismantlingInstructionsUrl, materialComposition[]
technicalSpecificationsobject
lifecycleAndPerformanceobjectPresent for categories that configure it (e.g. tyre labelling)
sustainabilityMetricsobject
batteryInformationobjectbattery_chemistry, battery_category, battery_status, weight_kg, …
batteryPerformanceobjectNested voltageSpecs, capacitySpecs, healthMetrics, energySpecs, temperatureRange, …
performanceMetricsobjectFlat performance metrics
socialResponsibilityobject
economicOperatorsarrayESPR Art. 4 operators (may be omitted)
technicalMetadataobjectInteroperability metadata (may be omitted)
complianceobjecteudr, gpsr, epr sub-sections
careInstructionsobjectPresent where configured (e.g. textiles)
environmentalDataobjectcarbon_footprint_total, water_consumption, plus sustainability metrics
productSafetyobjectceDeclarationUrl, riskAssessmentUrl, substancesOfConcern[], warnings, safetyLabels, …

Summary metrics and scores

KeyTypeNotes
carbonFootprintnumber | undefinedSummary total for hero cards: a single number, not a nested object
recycledContentnumber | undefinedSummary percentage
reparabilityScorenumber | undefined
overall_scorenumber
compliance_score / traceability_score / sustainability_scorenumber

Metadata and provenance

KeyTypeNotes
dpp_metadataobjectversion (integer), issued_at, valid_until, granularity, serial_number, last_updated
operatorobject{ type }
historyarrayPublic, regulatory-relevant lifecycle events
materialSourcingarrayConsent-filtered traceability records (may be omitted)
dataFreshnessarrayPublic-tier freshness metadata for dynamic (BMS-sourced) attributes
dynamicDataUpdatedAtstring | nullWhen public dynamic data was last updated; null if none
_configobjectThe category display configuration used to assemble the DPP (internal metadata)
_calculated_fieldsobjectServer-computed derived values
formDataobjectThe raw field_id → value bag (snake_case keys). For public callers this is access-tier gated: restricted values are stripped before serialisation
lastUpdatedAtstringPublish time (see Timestamps)
lastUpdatedstringTime 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:

FieldMeaning
lastUpdatedAtPublish time of the served version (the snapshot's publish time; falls back to first-approval time, then to now for legacy rows)
lastUpdatedWhen this API response was generated
dpp_metadata.issued_atWhen the DPP was issued
dpp_metadata.valid_untilValidity end, when set
dpp_metadata.last_updatedDate-time of latest update of the DPP (BatteryPass v1.3 attr #4)
dynamicDataUpdatedAtWhen 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 as dpp:passportVersion in the JSON-LD output.
  • dpp_metadata.version: the integer version counter (starts at 1, 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 formData key 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.