Optimus Mini · Standard GPS

Live Vehicle GPS

Put a phone or a GPS device in a vehicle, then read its live position, speed, and history from your own backend or website. One contract for every device type, over REST and WebSocket.

RESThttps://api.niyantha.in
WSwss://api.niyantha.in/ws
Update interval1–10 s
Step 1

Setup (once per vehicle)

  1. Create a Niyantha account and sign in to the dashboard.
  2. Add the vehicle under Vehicles (or POST /api/v1/session/vehicles, see Create Vehicle). Note its id.
  3. Install Optimus Mini on the phone that rides in the vehicle, sign in with the same account (SMS OTP or password), choose the vehicle, press Start streaming. Each phone becomes its own sensor, so one account can run a whole fleet. A Standard GPS device is attached from the dashboard and lands in the same APIs.
  4. Crew with their own accounts: open the vehicle in the dashboard → Crew Access and add the driver, conductor or cleaner by phone number (POST /api/v1/session/vehicles/:id/access { "contact": "+91...", "role": "driver" }). They sign in to Optimus Mini with their account, find the vehicle tagged with their role and attach their phone. Revoking detaches the phone at once. GET /api/v1/session/vehicles?scope=all lists owned and granted vehicles with access_role.
  5. Optional: create a public tracking link so a website can show the vehicle without credentials.

Fixes captured offline are queued on the phone and flushed in order when connectivity returns, so history has no gaps.

Step 2

Authentication

MethodUse it forHow
Session bearer tokenScripts acting as the accountPOST /api/v1/accounts/login → token; send Authorization: Bearer
S2S API keyYour server (Flask, Node, …)Create once: POST /api/v1/session/keys with ttl_seconds. REST headers X-API-Key / X-API-Secret; WS auth.login with authType: "s2s". See S2S guide.
Public tracking tokenA public page showing one vehicleNo credentials; the token in the link is the secret. Deactivate the link to rotate it.
Contract

The live payload

Every live surface (REST snapshot, account event, public event) uses the same location object.

GET /api/v1/session/vehicles/45/location
{
  "vehicle_id": 45,
  "vehicle": { "id": 45, "display_name": "College Bus 1", "vehicle_type": "van", "registration_number": "KA19AB1234" },
  "sensor": {
    "id": 128, "device_id": "mob_8fhs82aa", "sensor_code": "mob_8fhs82aa",
    "name": "Optimus Mini · Samsung SM-A155F", "sensor_type": "mobile", "status": "active",
    "battery_pct": 72, "last_seen": 1779201065
  },
  "location": {
    "lat": 13.34012, "lng": 74.74211,
    "speed_kmh": 38.2, "heading": 186, "accuracy": 4.5, "altitude": 21.3,
    "sensor_id": 128, "timestamp": 1779201065, "server_time": 1779201067, "age_s": 2, "moving": true
  },
  "server_time": 1779201067
}
lat, lngWGS-84 decimal degrees
speed_kmhSpeed in km/h as reported by the device
headingDegrees clockwise from north (0-359)
accuracyHorizontal accuracy in metres
altitudeMetres above sea level
timestampGPS fix time, unix seconds (device clock)
server_timeWhen Niyantha produced the payload, unix seconds
age_sserver_time - timestamp, clamped at 0; how stale the fix is
movingtrue when speed_kmh >= 3 (filters parking jitter)
device_idPublic sensor code of the device that produced the fix
battery_pctPhone battery 0-100 (mobile sensors only)

location is null until the first fix arrives; sensor is null when nothing is attached.

Pull

REST

Session (Bearer) or S2S (API key)

GET
/api/v1/session/vehicles/:id/location

Latest fix with derived fields. S2S: /api/v1/s2s/accounts/vehicles/:id/location

GET
/api/v1/session/vehicles

List vehicles (page, count, status, vehicle_type)

GET
/api/v1/session/vehicles/:id/telemetry/history?from=&to=

Points plus stats: total_distance_km, moving_time_s, idle_time_s, max/avg speed_kmh, overspeed instances, coverage gaps

PATCH
/api/v1/session/vehicles/:id/telemetry/settings

{ "overspeed_enabled": true, "overspeed_limit_kmh": 60 }

GET
/api/v1/session/vehicles/:id/telemetry/overspeed-alerts

Worker-created overspeed alerts

POST
/api/v1/session/vehicles/:id/tracking-links

{ "no_expiry": true } for a permanent public link; default expiry 24 h

GET
/api/v1/session/sensors

Phones and GPS devices registered on the account

Public (no auth)

GET
/api/v1/public/vehicle-tracking/:token

Vehicle summary + the live location object; the dashboard renders it at /track/vehicle/:token

Push

WebSocket

Frames are JSON { type, requestId, as, payload }. Authenticate once; live events are pushed without subscribing. Send { "type": "ping" } every 15–30 s.

Account / server socket

auth.login (S2S key; use authType "session" with a bearer token)
{ "type": "auth.login", "requestId": "a1", "payload": { "authType": "s2s", "token": "<key>", "metadata": { "secret": "<secret>", "app": "college-bus" } } }
EVENT
vehicle.location.updated

Every accepted fix for any vehicle of the account: { vehicle_id, sensor_id, device_id, location { …live payload…, battery_pct } }

EVENT
vehicle.telemetry.overspeed_alert.updated

Overspeed alert created or extended

WS
account.vehicle.location.get

{ "vehicle_id": 45 } → .ok with the REST live-location payload

WS
account.vehicle.telemetry.history

Same as the REST history call

Public tracking socket (no credentials)

auth.login with the tracking-link token
{ "type": "auth.login", "requestId": "t1", "payload": { "authType": "vehicle_tracking", "token": "<tracking-link-token>" } }
EVENT
public.vehicle.location.updated

{ vehicle_id, location { lat, lng, speed_kmh, heading, accuracy, altitude, timestamp, server_time, age_s, moving } } for that vehicle only

Copy-paste

Minimal consumers

Flask backend relaying live events (pip install websocket-client)
import json, os, threading, websocket

latest = {}

def on_message(ws, raw):
    msg = json.loads(raw)
    if msg.get("type") == "vehicle.location.updated":
        latest[msg["payload"]["vehicle_id"]] = msg["payload"]["location"]

def on_open(ws):
    ws.send(json.dumps({"type": "auth.login", "requestId": "a1", "payload": {
        "authType": "s2s", "token": os.environ["NIYANTHA_KEY"],
        "metadata": {"secret": os.environ["NIYANTHA_SECRET"], "app": "college-bus"}}}))

ws = websocket.WebSocketApp("wss://api.niyantha.in/ws", on_open=on_open, on_message=on_message)
threading.Thread(target=ws.run_forever, kwargs={"ping_interval": 20}, daemon=True).start()
# serve `latest` from your own Flask route, e.g. GET /bus/<vehicle_id>
React page on a public tracking link (no backend needed)
const ws = new WebSocket("wss://api.niyantha.in/ws");
ws.onopen = () => ws.send(JSON.stringify({ type: "auth.login", requestId: "t1", payload: { authType: "vehicle_tracking", token } }));
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "public.vehicle.location.updated") setPosition(msg.payload.location);
};
// initial paint: fetch(`https://api.niyantha.in/api/v1/public/vehicle-tracking/${token}`)