<pclass=sub>Platform reference · generated 2026-06-05 09:46 UTC from the live database (<code>tracksolid_db</code> @ twala.rahamafresh.com:5433)</p>
<divclass="note"><b>What this document is.</b> A current-state reference for the fleet telematics platform after the
map dashboards were moved off n8n onto the dedicated <code>dashboard_api</code> service (<code>fleetapi.rahamafresh.com</code>).
Covers data flow, deployment, the read-API, and the full database schema (tables, views, functions).</div><h2id=arch>1 · Architecture & data flow</h2><divclass=flow>
<pclass=muted><b>Consolidation 2026-06-10:</b> the backend collapsed from 7 services to 4 app services + db. The two pollers merged into a single <code>ingest_worker</code>; <b>pgbouncer</b> (dormant) and <b>Grafana</b> (redundant with FleetOps) were removed. Pipeline freshness — the one thing only Grafana surfaced — is now <code>reporting.v_ingest_health</code> (migration 19) via <code>dashboard_api</code><code>GET /health/ingest</code>.</p><h2id=mig>2 · The n8n → fleetapi migration</h2><p>The two map dashboards previously fetched data from n8n webhooks at
(already in <code>docker-compose.yaml</code>) once the branch is merged and the <code>fleetapi</code> domain is set in Coolify.</div><h2id=api>4 · Read-API reference (dashboard_api)</h2><pclass=muted>Base: <code>https://fleetapi.rahamafresh.com</code> · all responses JSON · CORS limited to the two SPA origins.</p><table><thead><tr><th>Method</th><th>Path</th><th>Params</th><th>Returns</th><th>Notes</th></tr></thead><tbody><tr><tdclass=mono>GET</td><tdclass=mono>/health</td><tdclass=mono>—</td><td>{status: ok}</td><td>Liveness probe.</td></tr><tr><tdclass=mono>GET</td><tdclass=mono>/webhook/live-positions</td><tdclass=mono>cost_centre?, acc_status?</td><td>{summary, geojson}</td><td>Live Positions map feed → reporting.fn_live_positions. ~80 vehicle point features.</td></tr><tr><tdclass=mono>GET</td><tdclass=mono>/webhook/live-positions/track</td><tdclass=mono>vehicle_number, hours(1-24)</td><td>GeoJSON Feature (LineString)</td><td>One vehicle's recent trail. Alias: /webhook/vehicle-track.</td></tr><tr><tdclass=mono>GET</td><tdclass=mono>/webhook/vehicle-track</td><tdclass=mono>vehicle_number, hours</td><td>GeoJSON Feature</td><td>Alias of live-positions/track (kept for compatibility).</td></tr><tr><tdclass=mono>GET</td><tdclass=mono>/webhook/fleet-dashboard</td><tdclass=mono>—</td><td>{drivers, cost_centres, cities, vehicles}</td><td>Filter options for the Fleet Intelligence UI.</td></tr><tr><tdclass=mono>POST</td><tdclass=mono>/webhook/fleet-dashboard</td><tdclass=mono>form-urlencoded: period|start_date|end_date, vehicle_numbers, driver, cost_centre, assigned_city</td><td>trips GeoJSON payload</td><td>Trips for the map → reporting.fn_trips_for_map. period preset: today|7d|30d|custom. Body parsed by Content-Type — the SPA posts <code>application/x-www-form-urlencoded</code>; JSON also accepted.</td></tr></tbody></table>
<divclass="note"><b>Fix (2026-06-05).</b> The POST handler originally read the body as JSON only, but the SPA sends <code>x-www-form-urlencoded</code>; the mismatch silently dropped every filter so the map always returned the whole fleet. Now parsed by Content-Type (<code>parse_qs</code> for form bodies). Commit <code>f1387d1</code>.</div><h2id=db>5 · Database schema</h2><h3id=s_tracksolid>tracksolid <spanclass=muted>· Live data — the single source of truth. Ingested from the Tracksolid API.</span></h3><pclass=muted>17 table(s) · 15 view(s) · 1 function(s) documented.</p><details><summary><spanclass=mono>tracksolid.alarms</span><spanclass=pill>table</span><spanclass=pill>344,356 rows</span></summary><p>Alarm events (alarm_type, alarm_name, alarm_time).</p><tableclass="cols"><thead><tr><th>Column</th><th>Type</th><th>Null</th><th>Default</th><th>Comment</th></tr></thead><tbody><tr><tdclass=mono>id</td><tdclass=mono>bigint</td><td>NOT NULL</td><tdclass=mono>nextval('alarms_id_seq'::regclass)</td><td></td></tr><tr><tdclass=mono>imei</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>alarm_type</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>alarm_time</td><tdclass=mono>timestamp with time zone</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>geom</td><tdclass=mono>geometry(Point,4326)</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>lat</td><tdclass=mono>double precision</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>lng</td><tdclass=mono>double precision</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>speed</td><tdclass=mono>numeric(7,2)</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>acc_status</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>updated_at</td><tdclass=mono>timestamp with time zone</td><td></td><tdclass=mono>now()</td><td></td></tr><tr><tdclass=mono>alarm_name</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>source</td><tdclass=mono>text</td><td></td><tdclass=mono>'poll'::text</td><td></td></tr><tr><tdclass=mono>severity</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td>Alarm severity level: critical | warning | info</td></tr><tr><tdclass=mono>geofence_id</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td>Tracksolid geofence ID if this is a geofence alarm</td></tr><tr><tdclass=mono>geofence_name</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td>Human-readable geofence name</td></tr><tr><tdclass=mono>acknowledged_at</td><tdclass=mono>timestamp with time zone</td><td></td><tdclass=mono></td><td>Timestamp when alarm was acknowledged by an operator</td></tr><tr><tdclass=mono>acknowledged_by</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td>Username or ID of operator who acknowledged the alarm</td></tr></tbody></table></details><details><summary><spanclass=mono>tracksolid.api_token_cache</span><spanclass=pill>table</span><spanclass=pill>1 rows</span></summary><p>OAuth2 token cache for the Jimi/Tracksolid API.</p><tableclass="cols"><thead><tr><th>Column</th><th>Type</th><th>Null</th><th>Default</th><th>Comment</th></tr></thead><tbody><tr><tdclass=mono>id</td><tdclass=mono>bigint</td><td>NOT NULL</td><tdclass=mono>nextval('api_token_cache_id_seq'::regclass)</td><td></td></tr><tr><tdclass=mono>account</td><tdclass=mono>text</td><td>NOT NULL</td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>access_token</td><tdclass=mono>text</td><td>NOT NULL</td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>refresh_token</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>app_key</td><tdclass=mono>text</td><td></td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>expires_at</td><tdclass=mono>timestamp with time zone</td><td>NOT NULL</td><tdclass=mono></td><td></td></tr><tr><tdclass=mono>obtained_at</td><tdcl