TracePass
Passports

Build 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.

POST/api/v1/registry/upload-file
Download OpenAPI 3.1
POST/api/v1/registry/upload-file

Build 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.

**Only passports that would pass go in.** The Registry fails the whole file on one bad entry, so every other passport comes back in `excluded` with its `reason`: `not_found`, `not_battery`, `not_published`, `registered_in_production`, `registration_in_progress` (being sent to an EN 18222 registry), `readiness_blocked` (the blocking findings are in `details`), `invalid_upi`, `duplicate_upi` (the same address as another passport in the file) or `over_limit` (past 100). `included` lists the passports in the file, each with its address.

Each included passport's address is frozen on the passport (`registryUpi`) and the preparation recorded (`registrationPreparedVia: "file"`). Save `xml` under `filename` and upload it unchanged: the name uses only the characters the Registry accepts. A preparation is not a registration: record the Registry's answer for each passport with `POST /api/v1/passports/{id}/registration`.

The response is JSON rather than raw XML, so it replays under `Idempotency-Key` and carries the exclusions. **Paid plans only** (`402` on Free). OAuth callers must act for an admin or owner. Counts one v1 write per passport included.

Headers

  • Authorizationrequired

    string

    `Bearer <token>` — either a `tp_` API key (Developer → API Keys; simplest, for server-to-server) or an OAuth 2.0 access token (Developer → OAuth Apps; for user-authorized apps, scoped + revocable). The Authentication page has the full OAuth flow and scope list.

    e.g. Bearer tp_REDACTED_xxxxxxxxxxxx

  • Idempotency-Key

    string

    UUID v4.

Body fields

  • passportIdsrequired

    string[] (24 hex chars each, 1–100)

    The passports to put in the file. Passports that would not pass are returned in `excluded` rather than failing the call.

    e.g. ["6650b2c3d4e5f6a7b8c9d0e1", "6650b2c3d4e5f6a7b8c9d0e2"]

Request

curl -sS -X POST \
  https://app.tracepass.eu/api/v1/registry/upload-file \
  -H "Authorization: Bearer tp_REDACTED_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5a8d2f17-9c4e-4b3a-8e61-0f2c7d9b4e35" \
  -d '{ "passportIds": ["6650b2c3d4e5f6a7b8c9d0e1", "6650b2c3d4e5f6a7b8c9d0e2"] }'

Response

{
  "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n…",
  "filename": "acme-batteries-dpp-registry-batch-2026-11-06.xml",
  "included": [
    {
      "id": "6650b2c3d4e5f6a7b8c9d0e1",
      "upi": "https://id.tracepass.eu/01/04012345000016/21/A1B2C3"
    }
  ],
  "excluded": [
    {
      "id": "6650b2c3d4e5f6a7b8c9d0e2",
      "reason": "readiness_blocked",
      "details": [
        "Required field \"Battery Carbon Footprint — Total\" has no value."
      ]
    }
  ]
}