Skip to main content

Observations & events

An observation is one detection from one sensor. POST a batch, fusion turns the stream into tracks, and everything the pipeline produces lands in a queryable event log.

Push observations, POST /api/ingest/obs

The body is a JSON array of observations in the frozen obs.v1 shape. Send a batch per flush, not a request per detection: the pipeline ticks per batch.

curl -X POST "https://<your-backend-host>/api/ingest/obs?source=live" \
-H "Content-Type: application/json" \
-d '[
{
"schema": "obs.v1",
"obs_id": "rf-north-000481",
"t": "2026-08-28T08:14:22Z",
"platform": { "id": "rf-north" },
"sensor": { "id": "rf-north-df", "modality": "rf" },
"detection": {
"class": "uav_multirotor",
"class_conf": 0.74,
"signal": { "modality": "rf", "freq_mhz": 5806.0, "bearing_deg": 206.0 }
},
"provenance": { "decoder": "my-bridge/1.2" },
"quality": { "confidence": 0.74, "bearing_sigma_deg": 4.0 }
}
]'
{ "ok": true, "data": { "accepted": 1 } }

The observation shape

FieldRequiredTypeMeaning
schemayesstringThe frozen wire kind: always obs.v1.
obs_idyesstringYour unique id for this detection.
tyestimestampWhen the sensor saw it (ISO-8601).
platformyesobjectThe emplacement or vehicle doing the sensing. id is required; geo (lat, lon, alt_m_agl) localises it.
sensoryesobjectThe sensor itself: id, and modality (radar, rf, eo, ir, acoustic, remote_id, adsb, telemetry, cot, or any other label, preserved verbatim).
detectionyesobjectThe estimate: optional class (frozen strings such as uav_multirotor, uav_fixedwing, bird, helicopter) with class_conf, position as geo or enu, vel_mps, sensor-relative rel (range_m, bearing_deg, elevation_deg, all three), optional cooperative_id, and the modality-specific raw signal.
provenanceyesobjectOpen, adapter-specific metadata. Carried through untouched, never read by domain logic.
qualityyesobjectPer-detection quality: confidence, geoloc_sigma_m, bearing_sigma_deg, rssi_dbm and friends, all optional.

A bearing-only sensor sends its bearing on the signal (bearing_deg on an rf or acoustic signal) and no position; fusion performs a bearing-space update rather than inventing a range. A presence-only sensor (one microphone, a CSI link) sends detection.signal.zone_radius_m on its signal and no geometry at all.

The source gate

Query paramValuesMeaning
sourcelive (default), simulatedWhich door the batch enters through. Simulated observations produce simulated tracks, kept apart from the real picture.

A batch from a real sensor never says simulated, and a simulation replay never says live. The picture keeps the two apart end to end.

Read the log, GET /api/events

Everything the pipeline persists (observations, tracks, alerts, plans, taskings, engagements, battle-damage assessments) is queryable back out of the event store:

curl "https://<your-backend-host>/api/events?kind=track,alert&since=2026-08-28T08:00:00Z&limit=500"
Query paramDefaultNotes
kindallComma-separated: obs, track, alert, coa, engagement, bda, swarm_command, tasking, tasking_status, threat, plan, influence.
sinceOnly events after this ISO-8601 timestamp (exclusive).
untilOnly events at or before this timestamp.
track_idOnly events about this track.
run_idOnly events from one simulation run.
foldlatest keeps only the newest event per object.
limit500Maximum rows.

Each element is the event's frozen wire shape (track.v1, alert.v1, …), exactly as it was persisted. This is the after-action surface: a run can be replayed, a track's history reconstructed, an alert's lifecycle audited, from this one route.