Sensor / partner API

Versioned at /v1. OpenAPI 3.1 spec is the contract. Endpoints land progressively per BUILD-PROGRESS.md — see the scientific plan for the full schedule.

API versions

/v1v1.0.0stable/v1/openapi.json

Live at https://api.mud2dust.com/v1/version — consumed by the dev-site at build time.

Versioning

Breaking changes get a new path prefix (/v2, /v3). Non-breaking additions land in the current version. Old versions stay live for ≥6 months after a successor ships, with a Sunset response header advertising the cutoff.

OpenAPI spec

The live spec for the running sensor-bridge is at https://api.mud2dust.com/v1/openapi.json (DNS for api.mud2dust.com lands when the sensor-bridge stack deploys; the spec is also served alongside development at the local dev port). An interactive explorer is queued for P1.7 iter 4.

Object types

Seven first-class shapes share auth, trust scoring, and privacy controls:

ShapeCoversEndpoint(s)
Observationtime-series point readingsPOST /v1/observations
Profilemulti-depth time-series (Sentek 9-depth, lysimeters)POST /v1/profiles
Samplediscrete lab results (gravimetric VWC, texture, OM, …)POST /v1/samples
Eventsingle drone / aircraft / PhenoCam capturePOST /v1/uploads/initiatecomplete
Collectionmulti-flight drone survey, multi-pass aircraft campaignPOST /v1/collections
Annotationgeotagged agronomist note, citizen photoPOST /v1/annotations
Boundaryfield / management zone / EC-mapped zonePOST /v1/boundaries

Auth

OAuth 2.0 + PKCE with per-shape scopes (stations:write, observations:write, etc.) lands in P3.6. During development the placeholder is an X-Contributor-Id header.

What's live today (P1.5)

POST /v1/stations
  body: { name, geom_internal: {type:"Point", coordinates:[lon,lat]},
          vendor, observed_properties: [...], depths_cm?: [...], logger?: {...} }
  → 201 { id: "stn_…", geom_public, geom_public_mode, ... }

POST /v1/observations
  body: [ { station_id, observed_property, value, unit, ts, depth_cm? }, ... ]
  → 202 { accepted, rejected, rejected_details: [{index, flags}] }

Trust model

Every contribution has a sensor_class (research / professional / consumer / diy) and an operator_class (researcher / agronomist_supported / farmer / hobbyist / unknown). The product of those (× installation quality) yields a training_weight. Contributions ≥ 0.5 are eligible to retrain the public calibration model; everyone else is a correction-taker with full feature parity but excluded from retrain. Users never see their tier as a number — only contribution health indicators.

Privacy / coordinate fuzzing

Every shape stores both geom_internal (exact, used for model training) and geom_public (jittered or aggregated). Default mode is 5km jitter; opt-in to 10km, county- aggregated, exact, or fully private (excluded from public surfaces). Jitter is deterministic per (contributor, external_id) so coords stay stable across requests.