Connect battery passport data to your systems.
REST API, CSV import and export, fixed passport addresses and all data fields. With a test key you can try everything without changing any data.
Overview
Four objects and four routes for exchanging data. A CSV file is enough to get started; the REST API is there for ongoing operation.
- CompanyYour account with users and plan
- ModelMaster data, once per battery model
- PassportOne battery with serial number and its own address
- EventRepair, repurposing or waste
| Route | What for | Status |
|---|---|---|
| CSV import | Take over serial numbers from ERP, MES or Excel | Available |
| CSV export | Bring passport IDs and addresses back into your systems | Available |
| Passport address and QR code | Create your own labels or link to passports | Available |
| REST API | Create models and passports automatically, without manual uploads | Available |
CSV import
AvailableThe usual route from your ERP: export a list and upload it to a model. Each new serial number gets a passport.
Material;Serialnummer;Fertigungsdatum;Charge;Werk;Status BX-48V;SN-0001;01.03.2027;L2027-03;Essen;frei BX-48V;SN-0002;01.03.2027;L2027-03;Essen;frei BX-48V;SN-0003;02.03.2027;L2027-03;Essen;frei
| Rule | Specification |
|---|---|
| Encoding | UTF-8 with or without BOM. Windows-1252 is detected and converted. |
| Delimiter | Semicolon, comma or tab. The first line decides. |
| Size | At most 100 MB per file, also as an Excel file (.xlsx). Files from 1 MB run in sections automatically. |
| Structure | One battery per row, no line breaks inside a field. |
| Header | Optional. With a header, columns can be in any order and extra columns are ignored. Without one, the order in the table below applies. |
| Column | Field | Recognised column names | Required | Rule |
|---|---|---|---|---|
| 1 | Serial number | seriennummer, serialnummer, serial, serial number, serialnumber, serial no, sn, serien-nr, seriennr, sernr | Yes | At most 120 characters: letters, digits, spaces and - _ . / : |
| 2 | Manufacturing date | herstelldatum, produktionsdatum, fertigungsdatum, manufacturing date, production date, date, datum | Optional | 2027-03-01, 01.03.2027, 1.3.2027, 01/03/2027, 1/3/2027 or 2027/03/01 |
| 3 | Batch | charge, chargennummer, batch, lot, los | Optional | At most 120 characters |
| 4 | Place of manufacture | herstellort, produktionsort, werk, place, plant, manufacturing place | Optional | At most 190 characters |
What happens on upload
- A preview appears first; nothing is saved yet.
- Faulty rows are reported with their line number and skipped.
- Duplicate serial numbers in the file and numbers already in this model are skipped.
- Passports are only created after you confirm, in a single operation. The import appears in the audit trail.
Check a file
AvailableCheck an export before you sign in. The same rules apply as for the import in the app.
The check runs only in your browser; the file is not uploaded. Whether a number already exists in the model is only shown by the preview in the app.
Material;Serialnummer;Fertigungsdatum;Charge;Werk;Status BX-48V;SN-0001;01.03.2027;L2027-03;Essen;frei BX-48V;SN-0002;01.03.2027;L2027-03;Essen;frei BX-48V;SN-0003;02.03.2027;L2027-03;Essen;freiCSV export
AvailableFor every model you can download the list of all passports, as UTF-8 with BOM and semicolon-separated. That brings passport IDs and addresses back into ERP, MES or label printing. The download requires signing in.
Seriennummer;Pass-ID;Pass-URL;Herstelldatum;Charge;Herstellort;Status SN-0001;7KM2XQ9RTA;https://app.digitalbatterypass.eu/p/7KM2XQ9RTA;2027-03-01;L2027-03;Essen;Original
Passport address and QR code
AvailableEvery passport has a fixed address. That is all a QR code, a link from a manual or an ERP entry needs.
https://app.digitalbatterypass.eu/p/{PASS-ID}
https://app.digitalbatterypass.eu/p/{PASS-ID}?lang=en- The passport ID has 10 characters and avoids easily confused ones such as 0, O, 1, I and L.
- The QR code contains only this address. If the data changes, it stays valid.
- In the app, the code is available per passport as SVG and PNG, along with label sheets for printing.
- Without signing in, the passport page shows only the public data. Search engines are asked not to index it.
- With a GTIN set, there is also a GS1 Digital Link.
Create your own labels from the export
# pip install "qrcode[pil]"
import csv
import qrcode
with open("paesse_BX-48V.csv", encoding="utf-8-sig", newline="") as f:
for row in csv.DictReader(f, delimiter=";"):
qrcode.make(row["Pass-URL"]).save(f'{row["Pass-ID"]}.png')
Passport as JSON in EU format
The same address gives people the page and programs JSON under EU schema 1.0.0, without sign-in and with public data only. If required data is missing, it is also missing from the JSON and the Registry would reject the passport.
curl -H "Accept: application/json" https://app.digitalbatterypass.eu/p/{PASS-ID}
curl https://app.digitalbatterypass.eu/p/{PASS-ID}?format=json
Data fields
AvailableThe app uses 67 fields under the official EU data schema for batteries, version 1.0.0. Send multi-value fields as a list; GET /fields returns the allowed values. The categories are lmt, industrie, ev, sli and geraet.
General information13
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Language of the texts | content_language | vocab | Public | Optional |
| Manufacturer (name) | manufacturer_name | text | Public | All passport categories |
| Trade name | manufacturer_trade_name | text | Public | Optional |
| Trademark | manufacturer_trademark | text | Public | Optional |
| Manufacturer (address) | manufacturer_address | text | Public | All passport categories |
| Manufacturer website | manufacturer_website | url | Public | Optional |
| Manufacturer email | manufacturer_email | email | Public | Optional |
| Product number | product_number | text | Public | Optional |
| Place of manufacture (city) | manufacturing_place | text | Public | Optional |
| Region | manufacturing_region | text | Public | Optional |
| Country of manufacture | manufacturing_country | vocab | Public | All passport categories |
| Weight | weight_kg | number · kg | Public | All passport categories |
| Commercial warranty | warranty_months | integer · Monate | Public | Optional |
Materials & substances8
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Battery chemistry | chemistry | list · vocab | Public | All passport categories |
| Hazardous substances | hazardous_substances | list | Public | All passport categories |
| Critical raw materials | critical_raw_materials | list · vocab | Public | All passport categories |
| Chemical symbol on the label | chemical_symbol | list · vocab | Public | Optional |
| Extinguishing agent | extinguishing_agent | text · lang | Public | All passport categories |
| Cathode composition | composition_cathode | text | Legitimate interest | All passport categories |
| Anode composition | composition_anode | text | Legitimate interest | All passport categories |
| Electrolyte composition | composition_electrolyte | text | Legitimate interest | All passport categories |
Performance & durability23
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Rated capacity | rated_capacity_ah | number · Ah | Public | All passport categories |
| Nominal voltage | nominal_voltage_v | number · V | Public | All passport categories |
| Minimum voltage | min_voltage_v | number · V | Public | All passport categories |
| Maximum voltage | max_voltage_v | number · V | Public | All passport categories |
| Voltage values apply from | voltage_temp_min_c | number · °C | Public | All passport categories |
| Voltage values apply up to | voltage_temp_max_c | number · °C | Public | All passport categories |
| Power (original) | rated_power_w | number · W | Public | All passport categories |
| Power limit | power_limit_w | number · W | Public | All passport categories |
| Power limit applies from | power_temp_min_c | number · °C | Public | Optional |
| Power limit applies up to | power_temp_max_c | number · °C | Public | Optional |
| Test of the temperature range when not in use | temp_test | text · lang | Public | All passport categories |
| Temperature range from | temp_min_c | number · °C | Public | All passport categories |
| Temperature range to | temp_max_c | number · °C | Public | All passport categories |
| Internal cell resistance | internal_cell_resistance_mohm | number · mΩ | Public | All passport categories |
| Internal pack resistance | internal_pack_resistance_mohm | number · mΩ | Public | All passport categories |
| Expected lifetime | expected_lifetime_cycles | integer · Zyklen | Public | lmt, ev |
| Cycle life reference test | cycle_life_test | text · lang | Public | lmt, ev |
| Capacity exhaustion threshold | capacity_threshold_pct | number · % | Public | ev |
| Initial round-trip efficiency | initial_rte_pct | number · % | Public | lmt, ev |
| Round-trip efficiency at half cycle life | rte_half_life_pct | number · % | Public | lmt, ev |
| Charge C-rate in the cycle test | cycle_charge_crate | number · 1/h | Public | lmt, ev |
| Discharge C-rate in the cycle test | cycle_discharge_crate | number · 1/h | Public | Optional |
| Rated energy | rated_energy_kwh | number · kWh | Public | Optional |
Carbon footprint & recycled content6
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Share of renewable content | renewable_content_pct | number · % | Public | All passport categories |
| Carbon footprint | carbon_footprint | number · kg CO₂e/kWh | Public | Applies later |
| Recycled cobalt content | recycled_cobalt_pct | number · % | Public | Applies later |
| Recycled lithium content | recycled_lithium_pct | number · % | Public | Applies later |
| Recycled nickel content | recycled_nickel_pct | number · % | Public | Applies later |
| Recycled lead content | recycled_lead_pct | number · % | Public | Applies later |
Conformity & labelling3
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Separate collection symbol present | separate_collection_symbol | boolean | Public | All passport categories |
| EU declaration of conformity (link) | doc_url | url | Public | All passport categories |
| Due diligence report (link) | due_diligence_url | url | Public | Applies later |
Waste prevention and take-back10
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| End-user role in waste prevention | end_user_prevention | text · lang | Public | All passport categories |
| End-user role in separate collection | end_user_collection | text · lang | Public | All passport categories |
| Separate collection points | collection_points | text · lang | Public | All passport categories |
| Take-back points | take_back_points | text · lang | Public | All passport categories |
| Preparation-for-reuse facilities | reuse_facilities | text · lang | Public | All passport categories |
| Preparation-for-repurposing facilities | repurposing_facilities | text · lang | Public | All passport categories |
| Waste battery treatment facilities | treatment_facilities | text · lang | Public | All passport categories |
| Safety instructions for waste batteries | waste_safety | text · lang | Public | All passport categories |
| Meaning of the label symbols | label_symbols_meaning | text · lang | Public | All passport categories |
| Impact of substances on environment and health | substances_impact | text · lang | Public | All passport categories |
Dismantling, repair & spare parts3
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Spare parts and replacement (link) | spare_parts_url | url | Legitimate interest | All passport categories |
| Dismantling instructions (link) | dismantling_url | url | Legitimate interest | All passport categories |
| Safety measures (link) | safety_url | url | Legitimate interest | All passport categories |
Test reports1
| Field | Technical name | Type | Visible to | Required |
|---|---|---|---|---|
| Compliance test reports (link) | compliance_report_url | url | Authorities and test bodies | All passport categories |
REST API
AvailableCreate models and passports directly from ERP, MES or BMS and report events. JSON over HTTPS, authentication by key. Base address: https://app.digitalbatterypass.eu/api/v1
In live operation, the REST API is part of the Enterprise plan. With Pro you only send BMS measurements. Test keys are available in every plan.
Get started in three steps
- Sign in as an administrator and open Settings, then API keys.
- Create a test key. It is shown only once, so keep it like a password.
- Check the connection with GET /me. If the API responds, the key and network path are correct.
export BATTERYPASS_KEY="bp_test_..." curl -s https://app.digitalbatterypass.eu/api/v1/me \ -H "Authorization: Bearer $BATTERYPASS_KEY"
One key, one company
The key determines which data can be reached. You create it in the app and it is shown only once.
Test keys save nothing
A test key checks each request as in live operation but changes no data and uses no quota.
Safe to retry
With the same Idempotency-Key, no second record is created even after an interruption. The document number from your ERP works well for this.
Passports are not deleted
The API has no DELETE for passports. The end of a battery’s life is reported as an event.
Test keys respond with status 200 and dry_run: true. They read real data but change nothing.
Endpoints
| Method | Path | What for |
|---|---|---|
| GET | /me | Connection test: company, plan, rights and remaining quota |
| GET | /fields | Field catalogue with requirements per category |
| GET | /models | List models, also by model code |
| POST | /models | Create a model |
| GET | /models/{id} | Read one model with data and missing required fields |
| PATCH | /models/{id} | Change master data |
| GET | /passports | List passports, also by serial number or change date |
| POST | /passports/batch | Create up to 1,000 passports in one call |
| GET | /passports/{id} | Read one passport with address and status |
| GET | /passports/{id}/events | Read a passport’s events |
| POST | /passports/{id}/events | Report an event, such as repair or waste |
| POST | /models/validate | Check a draft, save nothing |
| POST | /passports | Create one passport |
| PATCH | /passports/{id} | Change date, batch or place |
| GET | /passports/{id}/components | Read components |
| POST | /passports/{id}/components | Install a component |
| PATCH | /passports/{id}/components/{cid} | Change or remove a component |
| POST | /passports/{id}/components/{cid}/replace | Replace a component |
| GET | /passports/{id}/telemetry | Read measurement series |
| POST | /passports/{id}/telemetry | Report measurements |
| GET | /certificates | List evidence |
| GET | /certificates/{id} | Evidence with download link |
| GET | /suppliers | List suppliers |
| GET | /supplier-requests | List data requests |
| POST | /supplier-requests | Send a supplier request |
| GET | /supplier-requests/{id} | Read a request with supplied values |
| POST | /supplier-requests/{id}/accept | Accept supplied values into the model |
| GET | /audit | Read the audit trail |
Example: create passports from the ERP
curl -X POST https://app.digitalbatterypass.eu/api/v1/passports/batch \
-H "Authorization: Bearer $BATTERYPASS_KEY" \
-H "Idempotency-Key: erp-4711" \
-H "Content-Type: application/json" \
-d '{
"model_code": "BX-48V",
"on_conflict": "skip",
"items": [
{ "serial": "SN-0001", "manufactured_at": "2027-03-01", "batch": "L2027-03" },
{ "serial": "SN-0002", "manufactured_at": "2027-03-01", "batch": "L2027-03" }
]
}'
import os
import requests
res = requests.post(
"https://app.digitalbatterypass.eu/api/v1/passports/batch",
headers={
"Authorization": f"Bearer {os.environ['BATTERYPASS_KEY']}",
"Idempotency-Key": "erp-4711",
},
json={
"model_code": "BX-48V",
"on_conflict": "skip",
"items": [{"serial": "SN-0001", "manufactured_at": "2027-03-01"}],
},
timeout=30,
)
res.raise_for_status()
print(res.json()["created"])
const res = await fetch("https://app.digitalbatterypass.eu/api/v1/passports/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BATTERYPASS_KEY}`,
"Idempotency-Key": "erp-4711",
"Content-Type": "application/json",
},
body: JSON.stringify({
model_code: "BX-48V",
on_conflict: "skip",
items: [{ serial: "SN-0001", manufactured_at: "2027-03-01" }],
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log((await res.json()).created);
<?php
$ch = curl_init("https://app.digitalbatterypass.eu/api/v1/passports/batch");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BATTERYPASS_KEY"),
"Idempotency-Key: erp-4711",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"model_code" => "BX-48V",
"on_conflict" => "skip",
"items" => [["serial" => "SN-0001", "manufactured_at" => "2027-03-01"]],
]),
]);
echo curl_exec($ch);
Response
HTTP/1.1 201 Created
X-Request-Id: req_4f7a91c2d03e8b56
{
"created": 1,
"skipped": 1,
"skipped_serials": ["SN-0002"],
"items": [
{
"id": "7KM2XQ9RTA",
"serial": "SN-0001",
"status": "original",
"url": "https://app.digitalbatterypass.eu/p/7KM2XQ9RTA"
}
]
}
Errors
HTTP/1.1 422 Unprocessable Entity
{
"error": {
"code": "invalid_serial",
"message": "Serial number is missing or contains invalid characters.",
"field": "items[3].serial",
"request_id": "req_a03c5e17b92d4f68"
}
}
| HTTP | Code | Meaning |
|---|---|---|
400 | invalid_request | Request is not valid JSON or contains unknown fields |
401 | unauthorized | Key is missing or invalid |
403 | insufficient_scope | The key lacks the required right |
403 | plan_required | Live keys require the Enterprise plan; with Pro only for BMS measurements |
403 | email_not_verified | The company’s email address is not confirmed yet |
403 | quota_exceeded | The plan’s quota is used up |
404 | not_found | Object does not exist or belongs to another company |
409 | duplicate_serial | Serial number already exists in this model (with on_conflict: fail) |
409 | duplicate_model | The model code is already taken |
409 | idempotency_conflict | The Idempotency-Key was used for a different request |
413 | payload_too_large | The request is larger than 2 MB |
422 | invalid_serial | A serial number is invalid; the response names the field. Nothing is saved. |
422 | invalid_date | A date does not match the format YYYY-MM-DD |
422 | out_of_range | A measurement is outside the allowed range |
422 | invalid_gtin | The GTIN has a wrong length or check digit |
422 | validation_failed | Another value breaks the rules |
429 | rate_limited | Too many requests; retry after Retry-After |
500 | internal_error | An error on our side. Safe to retry with the same Idempotency-Key. |
Limits
| Requests per minute and key | Live 300, test 60 |
| Passports per call | 1.000 |
| Request size | 2 MB |
| Entries per list page | 100, at most 500 |
| Active keys per company | 10 |
| Live keys | Enterprise with all scopes, Pro only with the scopes for BMS measurements. Test keys are available in every plan. |
Planning more than 100,000 passports a year? Please talk to us in advance.
Permissions
Each key only gets the rights the system needs. If one is missing, the API responds with 403 and insufficient_scope. In the app you choose a template: All rights, ERP, BMS or Read only.
| Right | Allows |
|---|---|
models:read | Read models and check drafts |
models:write | Create and change models |
passports:read | Read passports, events and components |
passports:write | Create and change passports, report events, install and replace components |
telemetry:read telemetry:write | Read and report measurements |
certificates:read | Read and download evidence |
suppliers:read suppliers:write | Read suppliers and data requests, send and accept requests |
audit:read | Read the audit trail |
Requests and responses
Lists come in pages with data, has_more and next_cursor. Pass the value of next_cursor as cursor. Unknown fields are rejected so typos show up immediately. With PATCH, a missing field stays unchanged and null clears it.
| Header | Meaning |
|---|---|
Idempotency-Key | Retry without a second record, valid for 7 days. The ERP document number works well. |
X-Batterypass-Dry-Run: true | Even a live key only checks this request and saves nothing. |
X-Request-Id | ID of the call, also in errors and in the app’s log |
X-RateLimit-Limit X-RateLimit-Remaining | Quota for the current minute |
Idempotent-Replayed | true when a stored response was replayed |
SDK and API console
AvailableTwo lean clients without dependencies: they retry requests safely, page through lists themselves and split large volumes into calls of 1,000 passports. Both are single files to copy into your project. The API console is in the app under Settings, API keys. The libraries are part of the Enterprise plan.
from batterypass import Batterypass
bp = Batterypass("bp_test_...")
print(bp.me()["organization"]["plan"])
items = [{"serial": f"SN-{i:06}"} for i in range(5000)]
print(bp.create_passports_all("BX-48V", items))
for passport in bp.list_all("/passports", model_code="BX-48V"):
print(passport["serial"], passport["url"])
import { Batterypass } from "./batterypass.mjs";
const bp = new Batterypass({ apiKey: process.env.BATTERYPASS_KEY });
console.log((await bp.me()).organization.plan);
const items = Array.from({ length: 5000 }, (_, i) => ({ serial: `SN-${i}` }));
console.log(await bp.createPassportsAll("BX-48V", items));
for await (const passport of bp.listAll("/passports", { model_code: "BX-48V" })) {
console.log(passport.serial, passport.url);
}
Components
AvailableRecord which modules, cells or BMS are installed in a battery. A serial number can only be installed once at a time per passport. On replacement, the old part stays on record with its removal date.
curl -X POST https://app.digitalbatterypass.eu/api/v1/passports/7KM2XQ9RTA/components \
-H "Authorization: Bearer $BATTERYPASS_KEY" -H "Content-Type: application/json" \
-d '{"kind": "module", "label": "Modul 1", "serial": "M-0001", "supplier": "Zellwerk GmbH"}'
curl -X POST https://app.digitalbatterypass.eu/api/v1/passports/7KM2XQ9RTA/components/12/replace \
-H "Authorization: Bearer $BATTERYPASS_KEY" -H "Content-Type: application/json" \
-d '{"serial": "M-0002", "note": "Modul nach Defekt getauscht"}'
Allowed types are pack, rack, module, cell, bms and other.
Measurements
AvailableReport data from the BMS, individually or up to 1,000 measurements with items. Read the series with since and until. Measurements are available from the Pro plan. They appear in the app and for entitled parties, not on the public passport page.
curl -X POST https://app.digitalbatterypass.eu/api/v1/passports/7KM2XQ9RTA/telemetry \
-H "Authorization: Bearer $BATTERYPASS_KEY" -H "Content-Type: application/json" \
-d '{"recorded_at": "2027-03-01T10:00:00Z", "soh_pct": 94.5, "cycle_count": 120}'
| Field | Range |
|---|---|
recorded_at | ISO 8601 |
soh_pct | 0–100 |
soc_pct | 0–100 |
capacity_kwh | ≥ 0 |
remaining_capacity_ah | ≥ 0 |
internal_resistance_mohm | ≥ 0 |
cycle_count | ≥ 0, integer |
temp_min_c temp_max_c | ≥ −273 |
energy_throughput_kwh | ≥ 0 |
negative_event | deep_discharge, overheat, accident |
note | max. 500 |
Evidence
AvailableUpload test reports, declarations of conformity and similar documents in the app on the model: PDF, PNG or JPG up to 10 MB. Through the API you read the list and get a download link valid for 15 minutes. Each file has a SHA-256 checksum.
curl -s "https://app.digitalbatterypass.eu/api/v1/certificates?model_code=BX-48V" -H "Authorization: Bearer $BATTERYPASS_KEY" curl -s https://app.digitalbatterypass.eu/api/v1/certificates/42 -H "Authorization: Bearer $BATTERYPASS_KEY"
Suppliers and data requests
AvailableRequest values from suppliers. They get an email with a link valid for 30 days and enter only the requested fields, without an account. Supplied values only reach the model after your approval. Available from the Starter plan, at most 20 requests per hour.
curl -X POST https://app.digitalbatterypass.eu/api/v1/supplier-requests \
-H "Authorization: Bearer $BATTERYPASS_KEY" -H "Content-Type: application/json" \
-d '{"model_code": "BX-48V", "supplier_name": "Zellwerk GmbH", "supplier_email": "daten@zellwerk.example",
"fields": ["chemistry", "composition_cathode"], "message": "Bitte bis Monatsende"}'
curl -s "https://app.digitalbatterypass.eu/api/v1/supplier-requests?status=submitted" -H "Authorization: Bearer $BATTERYPASS_KEY"
curl -X POST https://app.digitalbatterypass.eu/api/v1/supplier-requests/7/accept -H "Authorization: Bearer $BATTERYPASS_KEY"
GS1 Digital Link
AvailableSet a GTIN with a valid check digit on the model. The GS1 address from GTIN and serial number then leads to the passport page and appears in the API as gs1_link. Your QR codes still contain the fixed passport address, but you can create your own codes from the GS1 link. You get the GTIN itself from GS1.
https://app.digitalbatterypass.eu/01/{GTIN}/21/{SERIENNUMMER}
https://app.digitalbatterypass.eu/01/04012345678901/21/SN-0001
Audit trail
AvailableGET /audit returns all logged changes of the company, in pages and optionally from a point in time with since. Changes via the API carry the key’s name. In the app, administrators also see the latest calls per key.
EU registry
Test in the real registry pendingThe app creates the key pair for your service account. The private key stays encrypted with us. The public key goes into the registry. Your company must first be verified there with a qualified seal.
Only released, complete passports are submitted, in batches of at most 100. App and API show the status. The registry reads the passport address as JSON; with its access token it also receives the protected views. Invalid tokens are rejected.
Built from the Commission’s technical documents of 30 Oct 2026 and tested against our own test server. The test in the real registry is still pending.
curl -X POST https://app.digitalbatterypass.eu/api/v1/registry/submissions \
-H "Authorization: Bearer $BATTERYPASS_KEY" \
-H "Content-Type: application/json" \
-d '{"model_code":"LMT-48V"}'
curl https://app.digitalbatterypass.eu/api/v1/registry/submissions \
-H "Authorization: Bearer $BATTERYPASS_KEY"
Release and proof
AvailableWith publish: false you create drafts. They cannot be reached publicly until you release them. Every change to master data becomes a new version linked by hash to the previous one. At /p/ID/proof anyone can recalculate the public state.
POST /api/v1/passports/batch {"model_code":"LMT-48V","publish":false,"items":[...]}
POST /api/v1/passports/PASS-ID/publish
GET /api/v1/models/42/versions
GET /p/PASS-ID/proof
Versions
The major version is in the path. Within v1, only fields and endpoints are added. Planned but not yet available: webhooks.
| Version | Date | Change |
|---|---|---|
1.2.0 | 2026-11-06 | Registry, release, versions, further measurements for the protected EU views |
1.1.0 | 2026-11-06 | Components, measurements, evidence, suppliers, audit, single passport, GS1, granular rights, dry-run header, OpenAPI and health endpoint |
1.0.0 | 2026-11-06 | Models, passports, events, keys, test mode, idempotency |
Frequently asked questions
Short answers for your integration.
How do I test without creating real passports?
With a test key. It checks everything as in live operation and saves nothing.
What should I do after a timeout?
Send the same request again with the same Idempotency-Key. No second record is created.
How do I find the passport for a serial number?
With GET /passports?serial=… and model_code if needed.
Can I delete passports via the API?
No. Report the end of life as an event with the status waste.
How many passports are possible?
1,000 per call, 300 calls per minute. Your plan and our hosting also set limits. If you plan more than 100,000 passports a year, please talk to us in advance.
Try the import with your own file.
The Pilot plan is free and covers one model and 25 passports per year.