REST API v1

REST API Documentation

Automate your battery passport workflow. Create, update, publish and export products via REST API. The passport lookup endpoints are aligned with the methods and REST conventions of EN 18222:2026; a complete conformity assessment including dependent standards is still open.

Authentication

Scale plan required. Generate keys in your organization settings.

Authorization: Bearer dpp_live_...

Base URL

120 requests/minute per organization.

https://app.dpphero.com/api/v1

Quick Example

curl -X POST https://app.dpphero.com/api/v1/products \
  -H "Authorization: Bearer dpp_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "LFP Module 48V",
    "serial_number": "BAT-2025-001",
    "battery_category": "industrial",
    "manufacturing_date": "2025-06-15",
    "battery_mass": 45.5
  }'

Available Endpoints

All endpoints require a Bearer token (API key). Available on the Scale plan.

Products

GET/products
POST/products
GET/products/{id}
PATCH/products/{id}
DELETE/products/{id}
POST/products/{id}/image
DELETE/products/{id}/image
POST/products/{id}/files

Battery Condition

Update dynamic BMS data like state of charge, cycle count and capacity fade via API.

GET/products/{id}/condition
PATCH/products/{id}/condition

Bulk Import

POST/products/bulk

Export

GET/products/{id}/export/json
GET/products/{id}/export/gefeg-json
GET/products/{id}/export/pdf
POST/products/{id}/preprint-qr
GET/products/{id}/export/qr

Organization

GET/organization
GET/facilities
GET/audit-log

Lists, filters and pagination

GET /products returns page by page. page starts at 1, per_page runs from 1 to 100 (default 25). You can filter by status and battery_category, search with search and sort by created_at, updated_at or name, ascending or descending. The response reports total and total_pages.

GET /products?page=1&per_page=25&sort=created_at&order=desc

Machine-readable specification

The complete interface as an OpenAPI document, available without signing in. Use it to generate client code or load the collection into your tool of choice.

openapi.json

GS1 Digital Link

Passports can also be addressed through GS1 identifiers: GTIN and serial number together denote exactly one battery, the way GS1 Digital Link prescribes. How the GTIN is written in the process is stated at the endpoints concerned; which those are is described in the full documentation inside your account.

Errors and status codes

Every error arrives as a JSON object with the same shape. On 429 the Retry-After header gives the wait in seconds.

400validation_errorInput incomplete or invalid; details names the field
401unauthorizedAPI key missing or invalid
403forbiddenKey may not address this resource
403plan_requiredFeature belongs to a different plan
404not_foundResource does not exist in your organization
409conflictValue already taken, for example a duplicate identifier
429rate_limit_exceededToo many requests; observe Retry-After
500internal_errorError on our side; pass the correlation_id to support
{
  "error": {
    "code": "validation_error",
    "message": "...",
    "details": { "name": ["Required"] },
    "correlation_id": "..."
  }
}

The correlation_id also travels in the X-Correlation-Id header. Quote it when you contact support and we can find the same event in our logs.

Full Documentation

The complete API reference with field definitions, example payloads, error codes, auto-fill rules and the entire product schema is available in your dashboard.

What to know before your first request

Authentication

Every request carries a bearer token in the Authorization header. The REST API belongs to the Scale plan; on smaller plans you work through the interface or the CSV import.

Reading page by page

List endpoints do not return everything at once. You control that through page (from 1, default 1) and per_page (1 to 100, default 25). The response carries page, per_page, total and total_pages. So you do not have to guess when it ends. Sorting runs through sort (created_at, updated_at or name) and order (asc or desc). Filtering runs through status, battery_category and search.

No webhooks yet

Event notifications do not exist at present. You fetch changes by polling. It makes sense to sort by updated_at and limit to the period since your last run. We write this down instead of hiding it. That way you do not build your integration on a promise that does not exist.

The export follows the Battery Passport data model in version 2.0. What sits in which field is decided by the field registry of the application, not by the call. The endpoint descriptions above and the docs tab in the account come from the same source. They therefore do not drift apart.