Ingest reference
Send BMS telemetry from any vendor. Rootd's ingest is vendor-neutral — it accepts whatever each brand emits and normalizes units, timing, and gaps into one twin.
Overview
Ingest is the one-way door for raw readings. You don't standardize hardware first — you point each feed at Rootd and the normalization layer resolves the differences. Writes are idempotent on (tenant, pack_id, ts), so safe retries never double-count.
tenant. Ingest is scoped at the boundary; a reading can only ever land in its own tenant's twin store.Transports
The same logical payload arrives over whichever transport a vendor speaks. Rootd ingests:
REST— the canonical example used throughout these docs.MQTT— publish readings to a tenant-scoped topic; same field schema.CAN— via an edge collector that frames raw bus data into readings.
Payload schema
| Field | Type | Description |
|---|---|---|
| tenant req | string | Your tenant identifier. Scopes the reading. |
| pack_id req | string | Stable identifier for the battery pack. |
| vendor req | string | Source BMS vendor key. Used to select the normalization profile. |
| ts req | string (ISO-8601) | Reading timestamp in UTC. Drives freshness. |
| metrics req | object | Raw measured fields (e.g. voltage_mv, current_ma, temp_c). Units are resolved on normalization. |
Send many at once with the batch endpoint:
01POST /v1/ingest/readings:batch02{03 "tenant": "northwind",04 "readings": [05 { "pack_id": "PK-0421", "vendor": "vendor_a",06 "ts": "2026-06-03T08:14:02Z",07 "metrics": { "voltage_mv": 53120, "current_ma": -8100, "temp_c": 31 } }08 ]09}Normalization
Rootd maps each vendor's raw metrics onto a single internal schema: units are converted, sign conventions aligned, and missing-but-derivable values computed. The output is the vendor-neutral twin — any BMS in, one twin out.
State-of-health is derived from charge/discharge history. State-of-charge is then estimated on read from SoH and the latest reading — it is never written back as a stored field.
Connectivity & freshness
Rootd models connectivity from the gap between now and the latest ts:
| Field | Type | Description |
|---|---|---|
| online | state | Recent reading within the expected interval. |
| stale | state | No reading for a configurable window; twin holds last-known values. |
| offline | state | Gap exceeds the offline threshold. Still queryable at last-known state. |
stale or offline and keeps serving last-known values with an honest freshness flag.Errors
| Field | Type | Description |
|---|---|---|
| 400 | bad_request | Malformed payload or missing required field. |
| 401 | unauthorized | Missing or invalid tenant credentials. |
| 409 | conflict | Non-idempotent replay with a conflicting value for the same key. |
| 422 | unprocessable | Unknown vendor profile — no normalization mapping available. |