Developers / ProtocolsSearch docs ⌘K
Reference

Protocols

Send telemetry from any EV-motorbike OEM, manufacturer, or supplier in its native wire format. One endpoint, many codecs, across three layers: GPS telematics, on-vehicle BMS/bus, and charging infrastructure.

Overview

Rootd's ingest gateway decodes the wire format a device already speaks and normalizes it into the vendor-neutral twin. You don't reflash firmware or build a translation layer. You point the feed at POST /v1/ingest/{protocol} and pick the codec by name. Battery readings flow into the digital twin; GPS fixes into the fleet map.

Three layers, one API. Telematics (GPS trackers): jt808, gt06, teltonika, gbt32960. BMS / bus: bms, modbus, jbd, daly, can. Infrastructure (charge/swap cabinets): ocpp. Plus a vendor-neutral generic_json for custom backends.

Get a token

Authentication is a bearer token scoped to your tenant. An operator mints one from the dashboard (Settings → API tokens) or via the API; the token carries the ingest role, so it can push telemetry but is still tenant-isolated. Treat it as a device secret.

mint an ingest token
01# Mint an API token (an operator does this once; it carries the ingest role,
02# scoped to your tenant). Or create one in the dashboard: Settings → API tokens.
03curl -X POST https://api.rootd.cc/v1/settings/tokens \
04 -H "Authorization: Bearer $OPERATOR_TOKEN" \
05 -H "Content-Type: application/json" \
06 -d '{"scopes":[],"ttlHours":720}'
07# → { "token": "<INGEST_TOKEN>" }

The endpoint

POST/v1/ingest/{protocol}ingest gateway
FieldTypeDescription
{protocol} reqpathCodec name: one of the catalog below (e.g. jt808, modbus, ocpp).
Authorization reqheaderBearer token with the ingest role (see above).
Content-Type headerJSON for generic_json/ocpp; binary protocols post raw bytes (no type needed).
device queryDevice id for protocols whose wire carries none (modbus, can, jbd, daly, ocpp). Ignored when the wire already identifies the device.
body reqrawThe native payload: a JSON document, or the raw protocol bytes (a single message or several concatenated). Max 1 MiB.
Base URL. Production: https://api.rootd.cc. The endpoint is idempotent: replaying a frame is safe (de-duplicated on tenant + device + timestamp), so a device can retry freely after a connectivity gap.

Device identity

Every reading is attributed to a device. Protocols that carry an id on the wire (a JT808 BCD phone number, a teltonika IMEI, a GB/T VIN, a GT06 login IMEI) use it automatically. Protocols that don't (raw Modbus registers, CAN frames, JBD/Daly BMS, OCPP) take the id from ?device=. Non-UUID ids are mapped to a stable battery-twin id; the raw id is preserved for the fleet map.

Protocol catalog

FieldTypeDescription
generic_json genericVendor-neutral JSON (object or array). Battery + GPS. Carries its own device id.
jt808 telematicsJT/T 808 GPS trackers (China standard). Binary, 0x7e-framed. Location + EV battery items.
gt06 telematicsGT06 / Concox / Jimi trackers. Binary 0x7878. GPS (device id from login or ?device=).
teltonika telematicsTeltonika Codec 8 / 8E (Europe). Binary AVL. GPS + battery via IO elements. id from ?device=.
gbt32960 telematicsGB/T 32960 China national NEV reporting. Binary ## frame. Battery + GPS + alarms. id = VIN.
bms bmsNW lithium protection board v2.8. Binary NW frame. Battery (voltage/current/temp/SoC/cells).
modbus bmsModbus RTU (fn 0x03). Universal BMS/charger fieldbus. Battery via register map. id from ?device=.
jbd bmsJBD / Xiaoxiang smart BMS. Binary 0xDD…0x77. Battery. id from ?device=.
daly bmsDaly smart BMS. Binary 13-byte frames. Battery. id from ?device=.
can busRaw CAN frames (J1939 / CiA-454 via signal map). Battery. id from ?device=.
ocpp infraOCPP 1.6 MeterValues from charge/swap cabinets. JSON. Battery/station. id from ?device= + connectorId.

Examples

generic_json: the simplest path; carries battery and GPS in one JSON document:

generic_json
01curl -X POST https://api.rootd.cc/v1/ingest/generic_json \
02 -H "Authorization: Bearer $INGEST_TOKEN" \
03 -H "Content-Type: application/json" \
04 -d '{"device":"BIKE-001","timestamp":"2026-06-24T09:00:00Z",
05 "v":63.2,"i":-4.5,"temp":29,"soc":0.71,
06 "lat":-1.286,"lon":36.817,"speed":18,"heading":75,"altitude":1680}'
07# → 202 {"acceptedReadings":1,"acceptedLocations":1}

Binary protocols: post the raw wire bytes (here, a JT808 0x0200 report):

jt808 (binary)
01# Binary protocols (jt808, gt06, teltonika, gbt32960, bms, modbus, jbd, daly, can)
02# post the raw wire bytes. Send hex through xxd, stream with --data-binary:
03echo '7e0200002e8612345678900001...7e' | xxd -r -p | \
04 curl -X POST https://api.rootd.cc/v1/ingest/jt808 \
05 -H "Authorization: Bearer $INGEST_TOKEN" \
06 --data-binary @-
07# → 202 {"acceptedReadings":1,"acceptedLocations":1}

Identity-less wires: supply the device with ?device= (here, Modbus):

modbus (binary + device hint)
01# Protocols whose wire carries no device id (modbus, can, jbd, daly, ocpp)
02# take it from the ?device= query param:
03echo '4e57002a60300002...68...' | xxd -r -p | \
04 curl -X POST 'https://api.rootd.cc/v1/ingest/modbus?device=PACK-7' \
05 -H "Authorization: Bearer $INGEST_TOKEN" --data-binary @-

OCPP: charge/swap cabinets post MeterValues:

ocpp 1.6 MeterValues
01curl -X POST 'https://api.rootd.cc/v1/ingest/ocpp?device=CP-001' \
02 -H "Authorization: Bearer $INGEST_TOKEN" -H "Content-Type: application/json" \
03 -d '[2,"uid","MeterValues",{"connectorId":1,"meterValue":[{
04 "timestamp":"2026-06-24T09:00:00Z","sampledValue":[
05 {"value":"63.5","measurand":"Voltage","unit":"V"},
06 {"value":"80","measurand":"SoC","unit":"Percent"}]}]}]'

Responses & errors

A successful POST returns 202 Accepted with the counts decoded from the payload. Control/heartbeat frames are accepted with zero counts.

success
01{ "acceptedReadings": 1, "acceptedLocations": 1 }
FieldTypeDescription
202 AcceptedDecoded and persisted (or queued).
401 UnauthorizedMissing/invalid bearer token.
403 ForbiddenToken lacks the ingest role.
404 Not FoundUnknown {protocol} or unknown tenant.
413 Payload Too LargeBody over 1 MiB.
422 UnprocessableDecode/validation failed: the payload is dead-lettered for inspection.
429 Too Many RequestsPer-tenant rate limit; honor Retry-After.
503 UnavailableDownstream temporarily unhealthy; retry.

Other protocols

Don't see your device? Three paths keep you covered:

  • Cheap GPS trackers (Queclink, Ruptela, Meitrack, H02, TK103…): bridge them through an open tracker server into generic_json.
  • OEM clouds (NIU, Yadea, Super Soco, Bosch eBike, Gogoro): forward their REST/webhook payloads to generic_json.
  • Custom wire format: add a codec via the protocol SDK. Implement one Decode function and register it. See the ingest reference.
Need a protocol added? Tell us the OEM/manufacturer and share a wire sample; most binary dialects are a few hours to onboard. Get in touch.