TracePass
Passports

Capture 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`.

POST/api/v1/passports/{id}/measurements
Download OpenAPI 3.1
POST/api/v1/passports/{id}/measurements

Capture 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`.

Every measurement is stored. The newest per field (by `measuredAt`) also becomes the passport's current value for that field and sets `dynamicDataAsOf` on the passport. A later-arriving older measurement is kept in history but does not overwrite a newer current value. Providing an `externalId` makes a measurement **idempotent**: a repeat submission is counted in `duplicates`, not stored or metered again. The `Idempotency-Key` header is also supported — same key + same body replays the cached response for 24 h; same key + different body returns 422. OAuth scope: `passports:write`.

`batteryStatus` is **not** a measurement field — lifecycle status changes go through the dashboard. A repurposed, remanufactured, or reused battery gets a new passport linked to the original via `lineage` on passport create. A field the Regulation keeps off the battery's category returns 422 `field_not_applicable` (e.g. `stateOfCertifiedEnergy` is off-limits for LMT batteries; the five Annex VII Part A remaining-capacity metrics are off-limits for EV batteries). An alternate addressing form exists at `POST /api/v1/passports/by-serial/{serial}/measurements` — same body, same response. If a serial is not unique within your account, add `?gtin=<gtin>` to disambiguate.

Measurements count against the plan's **monthly** measurement allowance (`maxMeasurementsPerMonth`), not the daily v1 write budget. Paid plans keep accepting and counting measurements past the allowance at no charge; the Free plan stops at its allowance. Viewing a passport is never metered.

Path parameters

  • idrequired

    ObjectId

    Passport ID.

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

    Optional idempotency key (UUID v4 or any opaque string ≤ 64 chars). Same key + same body replays the cached response for 24 h; same key + different body returns 422.

Request

curl -sS -X POST \
  https://app.tracepass.eu/api/v1/passports/6650b2c3d4e5f6a7b8c9d0e1/measurements \
  -H "Authorization: Bearer tp_REDACTED_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "measurements": [
      {
        "fieldKey": "stateOfHealth",
        "value": 96.4,
        "measuredAt": "2027-03-01T06:00:00Z",
        "externalId": "bms-7781-2027-03-01"
      },
      {
        "fieldKey": "numberOfFullEquivalentChargingCycles",
        "value": 112,
        "measuredAt": "2027-03-01T06:00:00Z"
      }
    ]
  }'

Response

{
  "accepted": 2,
  "storedCount": 2,
  "materializedCount": 2,
  "duplicates": 0,
  "measurementIds": [
    "6750a1b2c3d4e5f6a7b8c9d0",
    "6750a1b2c3d4e5f6a7b8c9d1"
  ]
}