---
title: "Push battery measurements (state of health, cycles, …)."
method: POST
path: "/api/v1/passports/{id}/measurements"
tags: ["Passports"]
---

# Push battery measurements (state of health, cycles, …).

`POST /api/v1/passports/{id}/measurements`

## Request

- Base URL: `https://app.tracepass.eu`
- URL: `https://app.tracepass.eu/api/v1/passports/{id}/measurements`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://app.tracepass.eu/api/oauth/authorize`, token URL `https://app.tracepass.eu/api/oauth/token`, scopes: `passports:write`)

Battery passports only (Art. 77 / Annex XIII 4 of Regulation (EU)
2023/1542): push over-life measurements from your own equipment,
e.g. a BMS reporting state of health. Up to 500 per call.

Every measurement is stored. The newest one per field (by
`measuredAt`) also becomes the passport's current value for that
field, and sets `dynamicDataAsOf`. An older measurement arriving
late is kept in the history but does not overwrite a newer value.
An `externalId` makes a measurement idempotent: a repeat is counted
in `duplicates`, not stored or metered again.

Accepted `fieldKey`s are the Annex XIII point 4 use-data fields
(state of health, fades, cycle counts, dynamic values, state of
charge, negative events, temperature history). `batteryStatus` is not
a measurement: lifecycle status changes go through the dashboard,
and a second-life battery gets a successor passport (`lineage`). A
field the Regulation keeps off this battery category (e.g. state of
certified energy on an LMT battery) is refused with
`field_not_applicable`. The passport must be published.

Measurements are metered per calendar period against the plan's
monthly allowance (`maxMeasurementsPerMonth`), not against the daily
v1 write budget. Past the allowance, paid plans keep accepting and
counting them at no charge; the Free plan stops at its allowance.
Viewing a passport is never metered.

## Headers

- `Idempotency-Key` string

## Request body

- MeasurementsInput
  - `measurements` MeasurementInput[], required
    - `fieldKey` string, required: An Annex XIII point 4 use-data field key of the battery template, e.g. `stateOfHealth`.
    - `value` unknown, required
    - `measuredAt` string, date-time, required: When the equipment took the measurement. Not in the future.
    - `externalId` string: Your idempotency key for this measurement; a repeat is reported as a duplicate, not stored again.
    - `unit` string

## Response `200`

Measurements accepted.

- MeasurementsCaptureResult
  - `accepted` integer
  - `storedCount` integer
  - `materializedCount` integer: How many became the passport's current value (newer than what it held).
  - `duplicates` integer: How many were skipped because their `externalId` had already been received (in this call or an earlier one).
  - `duplicateExternalIds` string[]
  - `measurementIds` ObjectId[]

## Other responses

- `400`: `invalid_field_key` (not an accepted measurement key, including `batteryStatus`), `invalid_value` (wrong type or out of the field's range) or `measuredAt_in_future`.
- `401`: Missing or invalid API key.
- `402`: The subscription is past due, unpaid or canceled. Creating new content is blocked until billing is resolved; published passports stay accessible.
- `403`: Plan-gate or workspace permission denied.
- `404`: Resource doesn't exist or is in a different workspace.
- `422`: `not_a_battery`, `passport_not_active` (not published), `field_not_applicable`, or an Idempotency-Key reused with a different body.
- `429`: Daily v1 write or passport-read budget exhausted. Resets at 00:00 UTC.

---

[API](https://skmtc.dev/tracepass/apis/tracepass-api.md) · [All operations](https://skmtc.dev/tracepass/apis/tracepass-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tracepass/apis/tracepass-api/revisions/60ddeb25638b?raw)
