Download OpenAPI specification:
Draft REST surface for the Asgard fleet-tracking mobile app, Release 1 — Core tracking. Scope covered (from the client's updated phasing):
Conventions
2026-09-29T08:15:00Z).cursor + limit and returns meta.nextCursor./api/v1). Breaking changes go to /api/v2.| username required | string |
| password required | string <password> |
object (DeviceInfo) |
{- "username": "reports",
- "password": "pa$$word",
- "device": {
- "platform": "ios",
- "appVersion": "string",
- "deviceId": "string",
- "pushToken": "string"
}
}{- "accessToken": "string",
- "refreshToken": "string",
- "tokenType": "Bearer",
- "expiresIn": 900
}| refreshToken required | string |
{- "refreshToken": "string"
}{- "accessToken": "string",
- "refreshToken": "string",
- "tokenType": "Bearer",
- "expiresIn": 900
}{- "id": "u_1042",
- "username": "reports",
- "displayName": "Reports User",
- "email": "reports@example.com",
- "account": {
- "id": "acc_7",
- "name": "Vanguarder Demo"
}, - "role": "fleet_manager",
- "permissions": [
- "vehicles.read",
- "history.read",
- "geofences.read",
- "share.create"
], - "visibleGroupIds": [
- "g_1",
- "g_4"
], - "timezone": "Europe/London",
- "units": {
- "distance": "km",
- "speed": "kmh"
}
}Returns only vehicles in groups the caller may see. Live status is embedded so the
list screen needs a single call. Use /live for map refreshes.
| q | string Free-text search (name, registration, driver, VIN) |
| groupId | Array of strings Comma-separated group IDs |
| status | Array of strings (MotionState) Items Enum: "moving" "stopped" "parked" "offline" |
| ignition | boolean |
| sort | string Default: "name" Enum: "name" "-name" "lastUpdate" "-lastUpdate" "status" |
| cursor | string |
| limit | integer [ 1 .. 500 ] Default: 50 |
{- "data": [
- {
- "id": "v_501",
- "name": "KX19 ABC",
- "registration": "KX19 ABC",
- "vehicleType": {
- "id": "vt_2",
- "name": "Rigid 18t"
}, - "groups": [
- {
- "id": "g_1",
- "name": "North Depot"
}
], - "driver": {
- "id": "d_9",
- "name": "A. Smith"
}, - "live": {
- "state": "moving",
- "ignition": true,
- "speed": 54,
- "heading": 182,
- "position": {
- "lat": 53.4808,
- "lon": -2.2426
}, - "address": "M60, Stockport",
- "geofences": [
- {
- "id": "gf_3",
- "name": "Stockport Depot"
}
], - "lastUpdate": "2026-09-29T08:14:52Z",
- "stale": false
}
}
], - "meta": {
- "nextCursor": "eyJpZCI6IjUwMSJ9",
- "count": 1,
- "total": 312
}
}| vehicleId required | string |
{- "id": "string",
- "name": "string",
- "registration": "string",
- "vehicleType": {
- "id": "string",
- "name": "string"
}, - "groups": [
- {
- "id": "string",
- "name": "string",
- "vehicleCount": 0,
- "parentId": "string"
}
], - "driver": {
- "id": "string",
- "name": "string",
- "phone": "string"
}, - "live": {
- "state": "moving",
- "ignition": true,
- "speed": 0,
- "heading": 0,
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofences": [
- {
- "id": "string",
- "name": "string"
}
], - "lastUpdate": "2019-08-24T14:15:22Z",
- "stale": true
}, - "vin": "string",
- "make": "string",
- "model": "string",
- "imei": "string",
- "deviceModel": "string",
- "odometer": {
- "total": 0,
- "today": 0,
- "source": "can"
}, - "engineHours": 0,
- "currentTrip": {
- "startedAt": "2019-08-24T14:15:22Z",
- "durationSeconds": 0,
- "distance": 0,
- "startAddress": "string"
}, - "sensors": [
- {
- "key": "string",
- "label": "string",
- "value": 0,
- "unit": "string",
- "category": "fuel",
- "updatedAt": "2019-08-24T14:15:22Z"
}
], - "navigation": {
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "asOf": "2019-08-24T14:15:22Z",
}
}Lightweight endpoint the detail screen polls every few seconds.
| vehicleId required | string |
{- "state": "moving",
- "ignition": true,
- "speed": 0,
- "heading": 0,
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofences": [
- {
- "id": "string",
- "name": "string"
}
], - "lastUpdate": "2019-08-24T14:15:22Z",
- "stale": true,
- "vehicleId": "string",
- "odometer": 0,
- "fuelLevel": 0,
- "batteryVoltage": 0,
- "gsmSignal": 0,
- "satellites": 0,
- "sensors": [
- {
- "key": "string",
- "label": "string",
- "value": 0,
- "unit": "string",
- "category": "fuel",
- "updatedAt": "2019-08-24T14:15:22Z"
}
]
}| vehicleId required | string |
{- "data": [
- {
- "key": "fuel_level",
- "label": "Fuel level",
- "value": 63.5,
- "unit": "%",
- "updatedAt": "2026-09-29T08:14:52Z"
}, - {
- "key": "tpms.axle1.left.pressure",
- "label": "TPMS A1 L pressure",
- "value": 8.7,
- "unit": "bar",
- "updatedAt": "2026-09-29T08:14:40Z"
}
]
}Returns the latest position of every visible vehicle (optionally filtered).
Pass since (the serverTime of the previous response) to get only vehicles that
have moved/updated since then — the app merges the delta into its marker set.
For fleets of thousands of units the app should pass bbox and zoom so the
server can return clustered markers instead of raw points.
| groupId | Array of strings |
| status | Array of strings (MotionState) Items Enum: "moving" "stopped" "parked" "offline" |
| bbox | string Example: bbox=-2.5,53.3,-2.0,53.6 minLon,minLat,maxLon,maxLat |
| zoom | integer [ 0 .. 22 ] |
| since | string <date-time> |
| cluster | boolean Default: false |
{- "serverTime": "2026-09-29T08:15:00Z",
- "vehicles": [
- {
- "id": "v_501",
- "name": "KX19 ABC",
- "state": "moving",
- "ignition": true,
- "speed": 54,
- "heading": 182,
- "position": {
- "lat": 53.4808,
- "lon": -2.2426
}, - "lastUpdate": "2026-09-29T08:14:52Z"
}
], - "clusters": [ ],
- "removed": [ ]
}Ordered positions between from and to (max 7 days per call; page with cursor).
simplify applies Douglas-Peucker with the given tolerance in metres so the map
draws a clean polyline; omit for full-fidelity replay. Data available for 24 months.
| vehicleId required | string |
| from required | string <date-time> |
| to required | string <date-time> |
| simplify | number Default: 0 Tolerance in metres (0 = none) |
| format | string Default: "json" Enum: "json" "geojson" "polyline" |
| cursor | string |
| limit | integer <= 20000 Default: 5000 |
{- "vehicleId": "string",
- "from": "2019-08-24T14:15:22Z",
- "to": "2019-08-24T14:15:22Z",
- "pointCount": 0,
- "distance": 0,
- "bbox": "string",
- "points": [
- {
- "t": "2019-08-24T14:15:22Z",
- "lat": 0,
- "lon": 0,
- "speed": 0,
- "heading": 0,
- "ignition": true,
- "odometer": 0,
- "altitude": 0
}
], - "meta": {
- "nextCursor": "string",
- "count": 0,
- "total": 0
}
}One call for the "History" screen's day view.
| vehicleId required | string |
| from required | string <date-time> |
| to required | string <date-time> |
{- "summary": {
- "tripCount": 0,
- "distance": 0,
- "drivingSeconds": 0,
- "stoppedSeconds": 0,
- "parkedSeconds": 0,
- "maxSpeed": 0,
- "avgSpeed": 0
}, - "items": [
- {
- "type": "trip",
- "start": "2019-08-24T14:15:22Z",
- "end": "2019-08-24T14:15:22Z",
- "durationSeconds": 0,
- "trip": {
- "id": "string",
- "vehicleId": "string",
- "driver": {
- "id": "string",
- "name": "string"
}, - "start": {
- "time": "2019-08-24T14:15:22Z",
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "odometer": 0
}, - "end": {
- "time": "2019-08-24T14:15:22Z",
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "odometer": 0
}, - "durationSeconds": 0,
- "distance": 0,
- "maxSpeed": 0,
- "avgSpeed": 0,
- "idleSeconds": 0,
- "inProgress": true
}, - "stop": {
- "id": "string",
- "vehicleId": "string",
- "type": "stop",
- "start": "2019-08-24T14:15:22Z",
- "end": "2019-08-24T14:15:22Z",
- "durationSeconds": 0,
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "inProgress": true
}, - "geofenceVisit": {
- "id": "string",
- "vehicle": {
- "id": "string",
- "name": "string",
- "registration": "string"
}, - "geofence": {
- "id": "string",
- "name": "string"
}, - "enteredAt": "2019-08-24T14:15:22Z",
- "exitedAt": "2019-08-24T14:15:22Z",
- "dwellSeconds": 0,
- "inProgress": true
}
}
]
}| vehicleId required | string |
| from required | string <date-time> |
| to required | string <date-time> |
| minDistance | integer Default: 0 Drop trips shorter than this many metres |
| cursor | string |
| limit | integer [ 1 .. 500 ] Default: 50 |
{- "summary": {
- "tripCount": 0,
- "distance": 0,
- "drivingSeconds": 0,
- "stoppedSeconds": 0,
- "parkedSeconds": 0,
- "maxSpeed": 0,
- "avgSpeed": 0
}, - "data": [
- {
- "id": "string",
- "vehicleId": "string",
- "driver": {
- "id": "string",
- "name": "string"
}, - "start": {
- "time": "2019-08-24T14:15:22Z",
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "odometer": 0
}, - "end": {
- "time": "2019-08-24T14:15:22Z",
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "odometer": 0
}, - "durationSeconds": 0,
- "distance": 0,
- "maxSpeed": 0,
- "avgSpeed": 0,
- "idleSeconds": 0,
- "inProgress": true
}
], - "meta": {
- "nextCursor": "string",
- "count": 0,
- "total": 0
}
}type=stop is ignition-on stationary (idling); type=parking is ignition-off.
Both include duration and the geofence the vehicle was in, if any.
| vehicleId required | string |
| from required | string <date-time> |
| to required | string <date-time> |
| type | string Default: "all" Enum: "stop" "parking" "all" |
| minDuration | integer Default: 0 Seconds |
| cursor | string |
| limit | integer [ 1 .. 500 ] Default: 50 |
{- "data": [
- {
- "id": "string",
- "vehicleId": "string",
- "type": "stop",
- "start": "2019-08-24T14:15:22Z",
- "end": "2019-08-24T14:15:22Z",
- "durationSeconds": 0,
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "inProgress": true
}
], - "meta": {
- "nextCursor": "string",
- "count": 0,
- "total": 0
}
}| tripId required | string |
| includeTrack | boolean Default: true |
{- "id": "string",
- "vehicleId": "string",
- "driver": {
- "id": "string",
- "name": "string"
}, - "start": {
- "time": "2019-08-24T14:15:22Z",
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "odometer": 0
}, - "end": {
- "time": "2019-08-24T14:15:22Z",
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "odometer": 0
}, - "durationSeconds": 0,
- "distance": 0,
- "maxSpeed": 0,
- "avgSpeed": 0,
- "idleSeconds": 0,
- "inProgress": true,
- "track": [
- {
- "t": "2019-08-24T14:15:22Z",
- "lat": 0,
- "lon": 0,
- "speed": 0,
- "heading": 0,
- "ignition": true,
- "odometer": 0,
- "altitude": 0
}
], - "stops": [
- {
- "id": "string",
- "vehicleId": "string",
- "type": "stop",
- "start": "2019-08-24T14:15:22Z",
- "end": "2019-08-24T14:15:22Z",
- "durationSeconds": 0,
- "position": {
- "lat": 0.1,
- "lon": 0.1
}, - "address": "string",
- "geofence": {
- "id": "string",
- "name": "string"
}, - "inProgress": true
}
], - "geofenceVisits": [
- {
- "id": "string",
- "vehicle": {
- "id": "string",
- "name": "string",
- "registration": "string"
}, - "geofence": {
- "id": "string",
- "name": "string"
}, - "enteredAt": "2019-08-24T14:15:22Z",
- "exitedAt": "2019-08-24T14:15:22Z",
- "dwellSeconds": 0,
- "inProgress": true
}
], - "fuelUsed": 0
}| q | string Free-text search (name, registration, driver, VIN) |
| bbox | string |
| includeGeometry | boolean Default: true |
| cursor | string |
| limit | integer [ 1 .. 500 ] Default: 50 |
{- "data": [
- {
- "id": "string",
- "name": "string",
- "description": "string",
- "colour": "#FF8800",
- "shape": "polygon",
- "geometry": {
- "type": "Polygon",
- "coordinates": [
- [
- [
- -2.25,
- 53.48
], - [
- -2.24,
- 53.48
], - [
- -2.24,
- 53.49
], - [
- -2.25,
- 53.49
], - [
- -2.25,
- 53.48
]
]
]
}, - "radius": 0,
- "centre": {
- "lat": 0.1,
- "lon": 0.1
}, - "groupIds": [
- "string"
]
}
], - "meta": {
- "nextCursor": "string",
- "count": 0,
- "total": 0
}
}| geofenceId required | string |
{- "id": "string",
- "name": "string",
- "description": "string",
- "colour": "#FF8800",
- "shape": "polygon",
- "geometry": {
- "type": "Polygon",
- "coordinates": [
- [
- [
- -2.25,
- 53.48
], - [
- -2.24,
- 53.48
], - [
- -2.24,
- 53.49
], - [
- -2.25,
- 53.49
], - [
- -2.25,
- 53.48
]
]
]
}, - "radius": 0,
- "centre": {
- "lat": 0.1,
- "lon": 0.1
}, - "groupIds": [
- "string"
], - "vehiclesInside": [
- {
- "id": "string",
- "name": "string",
- "registration": "string"
}
]
}| geofenceId required | string |
| from required | string <date-time> |
| to required | string <date-time> |
| vehicleId | string |
| cursor | string |
| limit | integer [ 1 .. 500 ] Default: 50 |
{- "data": [
- {
- "id": "string",
- "vehicle": {
- "id": "string",
- "name": "string",
- "registration": "string"
}, - "geofence": {
- "id": "string",
- "name": "string"
}, - "enteredAt": "2019-08-24T14:15:22Z",
- "exitedAt": "2019-08-24T14:15:22Z",
- "dwellSeconds": 0,
- "inProgress": true
}
], - "meta": {
- "nextCursor": "string",
- "count": 0,
- "total": 0
}
}| vehicleId required | string |
{- "asOf": "2019-08-24T14:15:22Z",
- "data": [
- {
- "id": "string",
- "name": "string",
- "enteredAt": "2019-08-24T14:15:22Z",
- "dwellSeconds": 0
}
]
}| vehicleId required | string |
| from required | string <date-time> |
| to required | string <date-time> |
| geofenceId | string |
| cursor | string |
| limit | integer [ 1 .. 500 ] Default: 50 |
{- "data": [
- {
- "id": "string",
- "vehicle": {
- "id": "string",
- "name": "string",
- "registration": "string"
}, - "geofence": {
- "id": "string",
- "name": "string"
}, - "enteredAt": "2019-08-24T14:15:22Z",
- "exitedAt": "2019-08-24T14:15:22Z",
- "dwellSeconds": 0,
- "inProgress": true
}
], - "meta": {
- "nextCursor": "string",
- "count": 0,
- "total": 0
}
}{- "minAppVersion": {
- "ios": "string",
- "android": "string"
}, - "features": {
- "liveStream": false,
- "geofences": true,
- "notifications": false
}, - "livePollIntervalSeconds": 10,
- "staleAfterSeconds": 600,
- "historyRetentionMonths": 24
}