Developers / Twin APISearch docs ⌘K
Reference

Twin API

Read the normalized twin and everything derived from it: pack health, rider projections, and station forecasts. All endpoints are read-only, tenant-scoped, and audited.

Overview

The Twin API is how your apps consume Rootd. It never mutates batteries or runs operations; it returns the twin and the intelligence on top of it, for your systems to act on. After you ingest a reading, these are the endpoints that prove it landed and surface what Rootd derived from it.

Base URL: https://api.rootd.cc. All responses are JSON with camelCase fields.

Auth & tenancy

Authenticate with the same bearer token you use for ingest (or any operator/rider/financier session token). The token is bound to one tenant, and that scopes every query: there is no cross-tenant read path and no tenant header to pass.

authenticated request
01# Tenant is carried by the token; there is no tenant header.
02curl https://api.rootd.cc/v1/twins \
03 -H "Authorization: Bearer $ROOTD_TOKEN"
Roles gate the views. The twin and operator views require the operator role; the rider view requires rider; the loan views require financier. A token without the role gets 403. A built-in signup carries all three.

List twins

GET/v1/twinsoperator

Returns every battery twin in your tenant, each with derived health and its latest reading (null until the pack has telemetry). State-of-charge is intentionally not a stored field; the response carries raw socRaw and soh so you derive an estimate on read.

list twins
01GET /v1/twins
02{ "twins": [ {
03 "batteryId": "BIKE-001",
04 "vendor": "vendor_a",
05 "chemistry": "nmc",
06 "soh": 90.4,
07 "cycleCount": 412,
08 "status": "active",
09 "connectivity": "online",
10 "latest": { "voltage": 53.12, "current": -8.1,
11 "temperature": 31, "socRaw": 0.64,
12 "ts": "2026-06-24T08:14:02Z" }
13} ] }
FieldTypeDescription
batteryId stringStable twin id for the pack.
soh numberState-of-health, derived from charge/discharge history.
connectivity enumModeled state: online · stale · offline.
status stringLifecycle status (e.g. active, retired).
cycleCount numberReported cycle count.
latest object | nullLatest reading: voltage (V), current (A), temperature (°C), socRaw (0..1), ts.
SoC is derived, never stored. Estimate state-of-charge from soh + the latest reading. Treat it as guidance that moves with each reading, never as a peer of soh.

Twin history

GET/v1/twins/{id}/historyoperator

Returns the hourly telemetry rollups for one twin as {"hourly":[…]}. A twin with no telemetry yet returns an empty array, never a 404, because "no readings" is a valid state.

Rider view

GET/v1/rider/me/viewrider

The rider projection: earnings retained, current pack, and a suggested next swap that minimizes travel plus wait. This is a recommendation; your app decides whether and how to surface it.

FieldTypeDescription
earnings numberEarnings retained after swap friction: the north-star metric.
currentBatteryId stringPack the rider is currently on.
nextSwapRecommendation objectSuggested swap window, station, and modeled friction saved.
batteries arrayOwned/assigned packs with soh and a computed soc (derived, not stored).

Read a specific rider with GET /v1/rider/{id}/view.

Station forecast

GET/v1/operator/stations/{id}operator

Projects charged-pack supply per station over the forecast horizon and attaches a peak- smoothing recommendation. Pass ?recompute=true to force a fresh forecast before the response.

station forecast
01GET /v1/operator/stations/kawangware_hub
02{
03 "stationId": "kawangware_hub",
04 "forecast": [
05 { "ts": "2026-06-24T09:00:00Z", "chargedPacks": 82 },
06 { "ts": "2026-06-25T02:00:00Z", "chargedPacks": 19 }
07 ],
08 "recommendation": { "signal": "reroute", "reason": "dry risk 02:00" },
09 "asOfTs": "2026-06-24T08:50:00Z"
10}
FieldTypeDescription
forecast arrayPer-slot projection: ts + chargedPacks with a confidence interval.
recommendation objectsignal (e.g. reroute / hold), reason, and an optional time window.
asOfTs stringWhen the forecast was computed (UTC).
staleSlots numberCount of horizon slots backed by stale inputs.

Compare forecast against realized inventory with GET /v1/operator/stations/{id}/accuracy.