Collection · worked example

How we collect — one driver, one shift

A real run through the live stack: every data point gathered for a single driver across a morning shift, and which source it came from.
M. Yılmaz · vehicle B-UB-1180 · Pixel 7a · morning of 1 Jun 2026
A Uber webhook B phone (UsageStats) C Fleet Dashboard CSV D derived by backend
The shift, moment by moment
08:00
A
Driver goes online
Uber's server POSTs a signed webhook the instant his status changes. We verify the HMAC, normalise, and store it.
POST /ingest/uber-status { driver_id:"drv_yilmaz", status:"online", location:{52.520,13.405}, timestamp:"08:00" }
→ row in driver_status_events (status + GPS + time)
08:12
→18
A
Accepts a request → enroute → pickup → on_trip
Four more webhooks as the lifecycle advances. At 08:18 a passenger is on board.
→ 4 rows in driver_status_events · backend folds them into a trip + online_session (D) — on_trip window starts 08:18
08:22
B
📱 Phone reports: TikTok in foreground, 60s
The on-device agent reads which app is on top (UsageStats) and POSTs it — package name + duration only, never content.
POST /ingest/device-usage [{ deviceId:"phone_yilmaz", ts:"08:22", foregroundPackage:"com.zhiliaoapp.musically", durationMs:60000 }]
→ row in device_usage_events · category DISTRACTING · falls inside the on_trip window
HIGH flag: distracting_app_on_trip — TikTok 60s while a passenger was on board
08:25
B
📱 Phone reports: Google Maps, 120s
Same channel — but Maps is an ALLOWED work app, so it's stored and produces no flag.
→ row in device_usage_events · category ALLOWED
○ no flag — negative control: not everything is flagged
08:40
→41
A
Drops off rider, stays online (idle)
dropoff closes the trip (22 min on-trip); a following online keeps the session open.
trip marked completed (D) · idle-but-online time now accrues
weekly
C
📄 Fleet-Dashboard export imported
Ops downloads the acceptance report CSV; the importer parses it and POSTs his rates.
POST /ingest/acceptance [{ driverId:"drv_yilmaz", periodDate:"2026-05-31", acceptanceRate:76, cancellationRate:6 }]
→ row in acceptance_imports
LOW flag: low_acceptance — 76% accepted (< 80% target). Cancellation 6% is fine.
Everything we collected for him (real counts from the run)
A
6
status events
online→…→dropoff + GPS
B
2
app-usage events
TikTok + Maps (no content)
C
1
acceptance row
76% / 6%
D
1 · 1
session · trip
22 min on-trip
D
2
coaching flags
1 HIGH distraction · 1 LOW acceptance
0
screenshots · audio · off-shift
— never collected
This was a real run against the live backend + Postgres (not a mock) — the rows above were actually written and the two flags actually raised by the rules engine. What's stored for a driver is exactly this: status + location, app package + duration, acceptance %, and the derived trips/flags. No screen content, no audio, nothing off-shift.