Passports
Create, update, suspend, archive, and bulk-import Digital Product Passports. Includes the parties block for economic-operator chains.
/api/v1/passportsCreate a passport
Create a single Digital Product Passport. The passport is bound to a product (productId) and identified by a GS1 GTIN + serial number that's unique within that GTIN. New passports start in `draft` status — fields are populated via subsequent PATCH calls and the passport is published from the dashboard once review is complete.
/api/v1/passports/{id}Get a single passport
Read one passport by ID. Default response includes the full passport — translatable fields carry their `sourceLocale` and a per-locale `translations` map. Pass `?lang=<locale>` to have the server resolve every field through the public-viewer chain (viewer-locale translation → source-locale value → English → first-applied → universal source) and return one resolved value per field; the `translations` map is dropped from lang-resolved responses.
/api/v1/passports/{id}/qrRender the passport QR
Returns a freshly-rendered QR code for the passport, encoding its effective identifier URI. For GS1 passports this is the GS1 Digital Link URI (`/01/<gtin>/21/<serial>`); for ISO 15459 it is the resolver path on the configured host; for iec61406, did, and doi passports it is the `/x/<scheme>/<encodedValue>` resolver path (e.g. `https://id.tracepass.eu/x/did/<encodedDid>`). All five EN 18219 schemes resolve to the same public viewer. Use this when you want our renderer (consistent quiet zone, error correction, optional branding) instead of encoding the URI yourself. Defaults to `image/svg+xml`; `?format=png` returns a PNG and `?format=json` returns a `{ result: "<svg>" }` wrapper for embedding.
/api/v1/passports/{id}/complianceCheck passport compliance
Returns a three-tier compliance verdict for one passport — `compliant`, `compliant_with_warnings`, or `incomplete` — together with regulation-cited findings, so an integration can gap-check a passport, fix the cited gaps, then call again to confirm. That read → fix → verify loop is the point: the response tells an agent exactly what to set next.
/api/v1/passports/{id}/registry-readinessCheck passport registry readiness
Returns whether a passport would pass the **EU DPP Registry's formal submission gate** — `{ ready, findings[] }`. This is the registry's *mechanical* pre-submission check, distinct from and complementary to the substantive `/compliance` verdict: a passport can be registry-ready yet not substantively compliant, and vice-versa. Call it before submitting to the registry to catch formal gaps early.
/api/v1/passportsList passports
Paginated list of passports the workspace owns. Filter by `productId`, `status`, or a free-text search across GTIN and serial number. Counts against the daily passport-read budget (`maxV1PassportsPerDay`); the response items use the same shape as the single-read endpoint but trimmed to the listing fields.
/api/v1/passports/{id}/fields/{key}Update one field on a passport
Patch a single field on a passport. The value is validated against the field key (must exist on the passport's template) and persisted with an audit trail entry naming the credential (`via API key <prefix>` or `via OAuth app <client>`) and, when the `X-Source` header is set, the calling client, e.g. `(mcp)`. Use this in preference to writing the whole `fields` map when you only need to update one number — it's a smaller write and the per-field audit entry is what dashboard reviewers see.
/api/v1/passports/{id}/parties/{role}Upsert an economic-operator party
Create or replace the Party for a single role on a passport. The role path segment determines the slot you're writing — `manufacturer`, `importer`, `authorisedRepresentative`, `distributor`, `recycler`, `producerResponsibilityOrg`. Sending the same role twice is an upsert: the existing block is replaced atomically.
/api/v1/passports/{id}/registry-upiGet the EU DPP Registry address for a battery passport
Returns the unique product identifier (UPI) to paste into the EU DPP Registry's online form for one published **battery** passport, as `{ upi, preparedAt }`. The economic operator registers the passport on the Registry website under its own EU Login (Product group: Batteries; Granularity: Item). TracePass prepares the address and records that it did; it never submits anything to the Registry.
/api/v1/registry/upload-fileBuild an EU DPP Registry upload file for battery passports
Builds the XML file the economic operator uploads on the EU DPP Registry website (DPP Registration → upload a file) for the **battery** passports you name, up to 100 per call. Registration is the operator's own act under its own EU Login; TracePass prepares the file and never submits it.
/api/v1/passports/{id}/registrationRecord the EU DPP Registry result for a battery passport
Records what the economic operator reports from the EU DPP Registry website for one published **battery** passport. TracePass records the operator's statement and does not verify it with the Registry. The body takes one of two shapes, chosen by `environment`.
/api/v1/passports/{id}/registrationUndo a mistaken EU DPP Registry record
Clears a result recorded with `POST /api/v1/passports/{id}/registration`; the required `environment` query parameter says which one. This changes nothing in the EU DPP Registry itself: it corrects what TracePass holds for the passport. The undo is recorded.
/api/v1/passports/{id}/place-on-marketSend a battery passport to an EN 18222 registry API
Sends a published **battery** passport to a registry that accepts the EN 18222 `registerDPP` call, using the registry API access your account has configured (an API key or a bearer token), so the entry is made under your own credentials. Battery passports only.
/api/v1/passports/{id}/suspendSuspend a passport
Reversible suspend. The public viewer flips to the suspended state page (HTTP 423 with structured body); QR scans effectively die without the URL going to 404. Use for recalls, disputes, internal holds, or product-quality investigations. Republish from the dashboard once resolved.
/api/v1/passports/{id}/archiveArchive a passport (irreversible)
**Irreversible.** Public viewer returns 404, the GS1 Digital Link URL stops resolving and the QR code dies for good, unless the passport has been forwarded to a new provider. Use ONLY for products that never shipped — archiving a passport for a product already in customers' hands breaks every QR scan they'll ever make of it.
/api/v1/passports/{id}/mark-for-deletionMark a passport for deletion
**Step one of two.** Deleting a passport takes two steps: mark it for deletion here, then call `DELETE /api/v1/passports/{id}` (a never-published `draft` or `in_review` passport can be deleted directly). The delete is a soft delete: nothing is erased, but the passport disappears from the API and from every public surface. Scanning its QR code shows a page saying the passport has ended, and its machine-readable formats answer `410 Gone`.
/api/v1/passports/{id}Delete a passport
**A passport marked for deletion → soft delete.** Deleting a passport that has been published takes two steps: first `POST /api/v1/passports/{id}/mark-for-deletion`, then this DELETE. Nothing is erased: the passport disappears from the API (every v1 call on it answers `404` from then on) and from every public surface. Scanning its QR code shows a page saying the passport has ended, and its machine-readable formats answer `410 Gone`. Returns the passport with `status: "deleted"` and fires the `passport.deleted` webhook.
/api/v1/passports/batchBatch-create passports
Create up to 100 passports in one call. Each item carries the same body shape as the single-create endpoint (`{ productId, gs1: { gtin, serialNumber }, parties? }`). Partial-success per item — each gets its own status in the response array.
/api/v1/passports/{id}/snapshotsList passport snapshots
Returns a paginated list of immutability **snapshots** for a passport, newest first. A snapshot is written on publish and after every change to a published, suspended, expired or archived passport — field values, translations, parties, status transitions, supplier answers, AI writes, restores — whenever what the passport asserts actually changed (EN 18221:2026 4.2). Each entry includes the snapshot id, version number, reason (e.g. `published`, `field_edit`, `status_change`, `baseline`), who caused it (`actor`, when known), timestamp, content hash, and whether the hash still verifies — `hashValid: false` indicates at-rest tampering.
/api/v1/passports/{id}/snapshots/{snapshotId}Get passport snapshot
Returns the full archival record for one immutability **snapshot**: the complete JSON-LD payload exactly as the passport stood when the snapshot was taken — on publish, or after any change to a published, suspended, expired or archived passport — the snapshot metadata (version, reason, `actor` — who caused the change — and timestamp), and content hash re-verification. To get the version valid at a given date instead, use `?at=` on **List passport snapshots**. `hashValid: false` means the stored payload has been modified since the snapshot was taken — indicative of at-rest tampering.
/api/v1/passports/{id}/condition-flagsGet condition flags
Returns the resolved **condition profile** for a passport — `Record<flagKey, { value, status, source }>`. Condition flags are reviewer-approved yes/no facts about a product that gate conditional legal duties. For battery passports under Regulation (EU) 2023/1542, the registered flags are: `hasBMS` (Battery Management System present), `rechargeable` (battery is rechargeable), `externalStorageOnly` (Art. 8 exemption applies), and `isStationaryBess` (Stationary Battery Energy Storage System). Categories with no flags registered return an empty profile.
/api/v1/passports/{id}/condition-flagsSet condition flags
Write one or more condition flags to a passport. The request body is `Record<flagKey, boolean | null>` — `true` or `false` sets the flag; `null` clears it. Only keys registered for the passport's category are accepted; unknown keys return 400 with a list of valid keys for that category.
/api/v1/passports/{id}/measurementsCapture measurements
**Battery passports only** (Regulation (EU) 2023/1542 Art. 77, Annex XIII point 4 use data). The passport must be published; a draft or suspended passport returns 422 `passport_not_active`. A non-battery passport returns 422 `not_a_battery`. Send 1–500 measurements per call, each as `{ fieldKey, value, measuredAt, externalId?, unit? }` — `measuredAt` is an ISO 8601 timestamp, must not be in the future; `value` must be at most **16 KB when serialised** (a larger value returns 400 `invalid_value`). Accepted `fieldKey`s are the Annex XIII point 4 use-data fields: `stateOfHealth`, `stateOfCertifiedEnergy`, `remainingCapacity`, `remainingPowerCapability`, `remainingRoundTripEfficiency`, `evolutionOfSelfDischargeRate`, `currentInternalResistancePack`, `capacityFade`, `powerFade`, `internalResistanceIncrease`, `dynamicRatedCapacity`, `dynamicPowerCapability`, `dynamicInternalResistance`, `dynamicEnergyRoundTripEfficiency`, `dynamicExpectedLifetimeCycles`, `numberOfFullEquivalentChargingCycles`, `numberOfChargingEvents`, `currentStateOfCharge`, `negativeEvents`, `temperatureConditionsHistorical`.
/api/v1/passports/{id}/measurementsList measurement history
Returns the full measurement history for a passport, newest received first (by `receivedAt`). Each entry is a `PassportMeasurement` object with `_id`, `passportId`, `fieldKey`, `value`, `measuredAt`, `receivedAt`, `externalId?`, `unit?`, `materialized` (whether this entry became the passport's current value), and `source`.
/api/v1/passports/{id}/measurements/latestGet latest measurements
Returns the most recent measurement for each accepted Annex XIII point 4 use-data field. The response is `{ data: { <fieldKey>: LatestMeasurement | null } }` — every accepted key is always present; a key is `null` when no measurement has been received for that field yet. A `LatestMeasurement` carries `value`, `measuredAt`, and optionally `unit` and `externalId`.