Create battery units (one passport per battery) on a published model
POST/api/v1/products/:id/units
Required scope: dpp:draft:write. A WRITE (HMAC + Idempotency-Key).
The same operation as the operator portal's unit-list upload. The
model must already be published (Approved) by a person in the
Traceable UI; this call adds unit identifiers under that published
model and never publishes or changes the model itself.
Up to 1,000 units per call (TOO_MANY_UNITS above that; send a
larger fleet as several calls). Serials are trimmed and
de-duplicated (first row wins). Idempotent per serial: a serial that
already exists comes back existing and is never minted twice, so a
retry with a new Idempotency-Key is still safe. A unit that already
exists keeps its stored batch (storedBatch). A serial or batch that
breaks the identifier rule is a per-row invalid result, never a
failed call. The plan's battery passport limit is checked for the
whole call before the first unit is created (PLAN_LIMIT_REACHED,
nothing created). Units are created and audited in chunks of 100; if
a call fails part-way, earlier chunks are kept and the same list can
simply be sent again. Returns 201 when at least one unit was created,
200 when every row already existed or was invalid.
Request
Responses
- 200
- 201
- 400
- 401
- 402
- 403
- 404
- 409
- 413
- 429
- 503
No new unit (every row existed or was invalid)
At least one unit created
400 — INVALID_JSON, VALIDATION_ERROR, VALIDATION (the {id} path segment is not a UUID), or IDEMPOTENCY_KEY_REQUIRED.
401 — INVALID_SIGNATURE or SIGNING_NOT_CONFIGURED (or bearer auth failure).
The plan's battery passport limit would be exceeded (PLAN_LIMIT_REACHED); nothing was created.
403 — missing scope (FORBIDDEN_SCOPE) or module (FORBIDDEN_MODULE).
404 — not found OR owned by another tenant (NOT_FOUND; IDOR-safe).
The model is not published (PRODUCT_NOT_PUBLISHED).
More than 1,000 units in one call (TOO_MANY_UNITS).
429 — RATE_LIMITED (per-key limit, reads and writes alike; fails closed,
so a limiter outage also returns 429). A limiter-down 429 is deliberately
indistinguishable from a quota 429. Carries Retry-After (seconds).
Response Headers
Seconds to wait before retrying (429 only).
Per-minute request ceiling for this key's tier.
Requests left in the current window (floored at 0).
Seconds until the window resets.
503 — the API is not enabled on this environment (FEATURE_DISABLED).