# Data collection & the probe flywheel

MapMap can turn navigation into a data asset **without ever collecting a trajectory**. Because our SDK carries the routing graph on the device and snaps every GPS fix to the road, a drive is reduced to road-segment statistics *on the phone*, and only those aggregates are uploaded. Raw GPS never leaves the vehicle. That is the privacy guarantee, the bandwidth win, and (because raw location traces are effectively impossible to anonymise while aggregate segment speeds are not) the thing that makes the resulting data lawful to package and sell.

This is opt-in, off by default, and consent-gated in code. The full legal doctrine (`docs/PROBE-DATA-LICENSING.md`) and the wire format (`docs/PROBE-SCHEMA.md`) ship in the source checkout; access by email: [hello@mapmap.ai](mailto:hello@mapmap.ai).

## What the device collects

The `ProbeCollector` in the on-device SDK (`sn-nav-core`) consumes the same guidance stream that drives turn-by-turn, and produces a `ProbeBatch`: per road segment reached, an observation count, mean and 85th-percentile speed, stopped time, and the delta to the posted speed limit, keyed to the *served route* and a day-of-week × time-of-day bucket. Plus coarse events (reroutes, off-route, predicted-vs-actual ETA) and one origin/destination record at ~5 km geohash resolution.

Three guarantees are enforced by the code and its test suite, not just promised:

- **No raw coordinates.** No latitude/longitude appears anywhere in a batch except the coarse OD geohash cells, and even those are derived from mid-trip positions, never the true trip ends. A test asserts it.
- **Trip-end suppression.** Observations within a configurable radius (default 500 m) of both trip ends are discarded before aggregation, via a bounded trailing window, so only a suppression-distance of recent positions is ever held in memory, never the whole trace.
- **Off by default.** `ProbeConfig.enabled` must be set explicitly; in deployment it is gated on the fleet customer's opt-in.

```kotlin
// Android: collect only if the fleet operator has opted in.
val collector = ProbeCollector(ProbeConfig(enabled = fleetOptedIn, tripEndSuppressionM = 500.0))
collector.setVehicleClass("truck_40t")   // a class only, never a registration, VIN or driver
collector.setRouteRef(route.id)
// feed each guidance update…
collector.observe(update, fix.lat, fix.lon, fix.speedMps, fix.timestampMs)
// at arrival, upload the aggregate: kilobytes, no trajectory
val batch = collector.finish(arrived = true, timestampMs = now)
post("$BASE/v1/probe", batch.toJson())
```

## Ingest

`POST /v1/probe` accepts a `ProbeBatch` (gzip or plain JSON). It requires the calling key to carry the `probe_opt_in` capability; un-opted keys get `403`. Contributions are **never charged** (metered as free). A batch with the wrong `probe_format`, or whose numeric fields look like raw coordinates, is rejected. On a deployment with no probe store configured the endpoint returns `501`. See [`PROBE-SCHEMA.md`] for the exact shape.

## What it becomes

The server resolves each route-relative segment to a **stable edge ID** (`sn-stableid`, permanent across monthly territory rebuilds) and feeds:

- **Live traffic**: 5-minute rolling segment speeds into the traffic overlay, filling the urban streets that motorway-only feeds miss.
- **Historical speed profiles**: weekly day-of-week × time-of-day profiles into the predicted-traffic build stage.
- **OD matrices**: published only above a k-anonymity threshold, at coarse cells.
- **ETA accuracy**: the calibration set behind per-tenant ETA correction, and the marketing number.

## Agents can report map issues

Agents building on the [MCP server](/docs/mcp) get a `report_map_issue` tool: when an agent observes the live world disagreeing with the map (a closed road, a wrong restriction, a bad speed limit), it queues a first-party correction for review. It never edits routing on the spot, and (being an observation, not an extract of OSM) it stays licence-clean.

## Agents can send an integration retro

The same consent-first rule governs the other thing agents send us: an **integration retro**, submitted once via the MCP `submit_integration_retro` tool or `POST /v1/feedback` after a MapMap integration works or is abandoned. It is how docs gaps and SDK friction reach us instead of quietly costing someone an afternoon.

**Exactly what is sent** (these fields, and nothing else):

| Field | Contents |
|---|---|
| `what_built` | One or two sentences on what was built (required, ≤500 bytes; roughly 500 characters in plain English, fewer for accented or non-Latin text) |
| `problems[]` | Up to 20 × `{area, description, workaround_found}`; `area` is one of `sdk`, `api`, `mcp`, `docs`, `billing`, `self-host`, `other` |
| `gotchas[]` · `wins[]` · `docs_gaps[]` | Up to 20 short entries each |
| `agent_name` · `sdk_version` | Optional: which agent submitted, which SDK version it built against |

Alongside the payload we store the submitting API key's id and a timestamp, enough to join a retro to that key's own error history and to rate-limit the endpoint at 5 per key per day.

The guarantees, again enforced by the schema rather than promised:

- **The developer decides.** An agent should only submit if you have approved sending feedback to MapMap. That condition is stated in the tool description your client shows you at the approval prompt, in the agent skills and in `llms.txt`. Decline the tool call and nothing is sent; there is no default-on path.
- **Never your conversation or code.** The schema has no field for transcripts, prompts, source code, file contents or coordinates, and the gateway rejects payloads that do not match it. An agent physically cannot send its conversation through this channel.
- **Never model training.** Retro content is not used to train, fine-tune or evaluate any machine-learning model, and is not shared beyond the EU infrastructure storing it. Individual retros are never published; only aggregate themes.
- **Retention and deletion.** Retros are kept 24 months, then deleted. To have yours removed sooner, email matt@mapmap.ai from the address on the key.

The full programme terms are at [Agent Feedback Programme](/legal/agent-feedback), and everything MapMap collects server-side (usage counters, error classes, activation events) is enumerated in the [telemetry disclosure](/legal/telemetry).

## The principle

Everything here is aggregate-first and consent-first *because* that is what makes it both trustworthy and sellable. The full commercial and legal reasoning, covering why aggregated insight products (not raw-data marketplaces) are where map data earns money and the day-one contract clauses required before any of this ships, is in the repo's `DATA-AND-MOAT-STRATEGY.md`.
