Telemetry
Telemetry
Contribute anonymous GPS trips that Navvo map-matches and aggregates into the live/typical traffic layers, and read back aggregated traffic data. If your app has its own location stream (delivery drivers, ride-hail, fleet), submitting it here makes traffic better for everyone — including your own ETAs.
Scope required: telemetry
{ lat, lng } fields; speed is in meters per second; capturedAt is an ISO-8601 timestamp. All traffic output speeds are km/h, and output geometry is GeoJSON [lng, lat].POST /telemetry/trips
Submit a trip. The trip is stored and queued for asynchronous map-matching (via the Navvo routing engine); the call returns immediately.
Request body
metadata.consentCaptured = false to flag a trip as missing consent (it is still accepted but counted in the health summary). Consent is assumed true otherwise.Each points[] element:
| Field | Type | Required | Notes |
|---|---|---|---|
lat | number | yes | Latitude, −90..90. |
lng | number | yes | Longitude, −180..180. |
speed | number | no | meters/second. |
heading | number | no | Degrees. |
capturedAt | string | no | ISO-8601; defaults to now. |
lat/lng must be present numbers within range, speed/heading numeric, and a trip carries at most 5000 points. Out-of-range or malformed points return 400 with a validation array.{
"userId": "8b1f5c2e-3d4a-4e2b-9f10-7c2a1b6d9e44",
"metadata": { "consentCaptured": true, "device": "ios-17" },
"points": [
{ "lat": 31.9500, "lng": 35.9100, "speed": 12.1, "heading": 90, "capturedAt": "2026-05-16T12:00:00Z" },
{ "lat": 31.9512, "lng": 35.9123, "speed": 13.4, "heading": 88, "capturedAt": "2026-05-16T12:00:05Z" }
]
}
Response (201)
{ "accepted": true, "tripId": "f0a1c3d5-…", "jobId": "12" }
The trip is processed in the background: matched to the road network, then per-edge observed speeds feed the traffic speed profiles that power the Traffic endpoints. status becomes matched or failed.
POST /telemetry/errors
Report client-side crashes and errors, batched. A lightweight crash-reporting sink for your Navvo-powered apps (web, Android, iOS): send caught exceptions or fatal crashes and Navvo stores them for triage.
Authorization: Bearer <token>, the report is attributed to that verified user; otherwise it is anonymous. A client-supplied user id is never trusted. This route is throttled to 30 requests/min per IP.Request body
Each errors[] element:
| Field | Type | Required | Notes |
|---|---|---|---|
platform | string | yes | web, android, or ios. |
message | string | yes | Error message (max 2000 chars). |
stack | string | no | Stack trace (max 8000 chars). |
appVersion | string | no | Your app version. |
deviceModel | string | no | Device model. |
osVersion | string | no | OS version. |
context | object | no | Free-form structured context (screen, state, breadcrumbs). |
occurredAt | string | no | ISO-8601 timestamp; defaults to receipt time. |
{
"errors": [
{ "platform": "android", "appVersion": "3.2.1", "deviceModel": "Pixel 8",
"osVersion": "Android 15", "message": "TypeError: cannot read property 'lat' of null",
"stack": "at MapView.render (map.js:214)…", "context": { "screen": "navigation" },
"occurredAt": "2026-07-19T09:20:00Z" }
]
}
Response (201)
{ "ok": true, "accepted": 1 }
accepted is the number of stored error rows. Items missing platform/message or exceeding a length cap return 400 with a validation array. Stored reports are retained for a limited window (30 days by default) and then swept.
GET /telemetry/trips
admin/super_admin role (Bearer JWT); other callers receive 403. It was previously readable in keyless mode and exposed contributor ids and first/last GPS points.List the most recent trips (no pagination). Each item is a trip with status, consentCaptured, and a routeSummary (point/edge counts and first/last point); the raw points are not included.
GET /telemetry/health
Ingestion quality summary over the most recent 500 trips.
{
"totalTrips": 500,
"matchedTrips": 472,
"failedTrips": 18,
"consentMissingTrips": 3,
"sampleRate": 0.944,
"generatedAt": "2026-06-09T10:15:00.000Z"
}
GET /telemetry/daily
Trip counts bucketed per UTC day over the last 365 days.
[ { "day": "2026-06-09", "trips": 14 }, { "day": "2026-06-08", "trips": 31 } ]
GET /telemetry/traffic-overlay
The aggregated traffic layer as a GeoJSON FeatureCollection of road-edge speed profiles with congestion classification — the data source behind the map traffic layer.
Query parameters
current (default; last ~6h) or typical (all-time per-edge averages).west,south,east,north spatial filter.{
"type": "FeatureCollection",
"hasData": true,
"mode": "current",
"features": [
{
"type": "Feature",
"geometry": { "type": "LineString", "coordinates": [[35.911, 31.951], [35.914, 31.952]] },
"properties": { "edgeId": "e:128374", "congestion": "moderate", "averageSpeedKph": 28.4, "freeFlowSpeedKph": 55, "sampleCount": 4 }
}
],
"metadata": { "layerType": "traffic", "source": "navvo-telemetry-speed-profiles", "legend": { "light": { "color": "#188038" }, "moderate": { "color": "#D9A441" }, "heavy": { "color": "#D94B4B" }, "blocked": { "color": "#8B1A1A" } } }
}
congestion ∈ light | moderate | heavy | blocked | unknown.
Tiled access
For bounded loading the overlay is also tile-addressed:
| Endpoint | Returns |
|---|---|
GET /telemetry/traffic-overlay/tile-cover | List of tile URLs covering a bbox (mode, bbox, zoom, maxTiles). |
GET /telemetry/traffic-overlay/tiles/{z}/{x}/{y} | The overlay for one tile, as GeoJSON. |
GET /telemetry/traffic-overlay/vector/{z}/{x}/{y} | The overlay for one tile, as a binary vector tile (application/x-protobuf, layer navvo_traffic). |
.../vector/{z}/{x}/{y} route to add traffic as a native Navvo Maps vector source; use the GeoJSON tile route if you prefer to style features yourself.