{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://farmsense.daboodesigns.com/docs/observer-webhook.schema.json",
  "title": "FarmSense observer reading",
  "description": "The JSON body of a single reading POSTed by the FarmSense observer to a receiver endpoint. One object per request (never an array). Extra/unknown fields may be added in future and MUST be ignored, so additionalProperties is true.",
  "type": "object",
  "required": ["node_id", "moisture_pct"],
  "additionalProperties": true,
  "properties": {
    "node_id": {
      "type": "string",
      "minLength": 1,
      "description": "Stable sensor id: first 12 hex chars of the sensor's mesh public key (or 'sim-01' in sim mode). Always present and non-empty. Use as the device key.",
      "examples": ["a1b2c3d4e5f6", "sim-01"]
    },
    "node_name": {
      "type": "string",
      "description": "Human-readable sensor name; equals node_id when the sensor is unnamed.",
      "examples": ["North Field", "a1b2c3d4e5f6"]
    },
    "ts": {
      "type": "string",
      "format": "date-time",
      "description": "ISO-8601 UTC timestamp from the device clock. OPTIONAL — omitted when the device clock is unset. UNRELIABLE (clock drift): treat the receiver's own arrival time as authoritative and use ts only as informational device-reported time.",
      "examples": ["2026-08-23T15:42:10Z"]
    },
    "moisture_pct": {
      "type": "object",
      "description": "Soil moisture percentage per depth. REQUIRED with at least one entry. Keys are depth labels '6in'/'12in'/'18in'; an unmapped mesh channel appears as 'chN'. WHICH depths are present varies by how many probes the sensor has — do not assume all three.",
      "minProperties": 1,
      "additionalProperties": { "type": "number" },
      "examples": [{ "6in": 31.2, "12in": 40.1, "18in": 45.0 }, { "6in": 28.7 }]
    },
    "temp_c": {
      "type": "object",
      "description": "Per-depth soil temperature in degrees Celsius. OPTIONAL — omitted entirely when no temperatures were read; may hold a subset of the moisture depths. Keys match moisture_pct.",
      "additionalProperties": { "type": "number" },
      "examples": [{ "6in": 24.3, "12in": 22.1, "18in": 20.8 }]
    },
    "battery_v": {
      "type": "number",
      "description": "Sensor battery voltage in volts. OPTIONAL.",
      "examples": [4.01, 3.9]
    }
  },
  "examples": [
    {
      "node_id": "a1b2c3d4e5f6",
      "node_name": "North Field",
      "ts": "2026-08-23T15:42:10Z",
      "moisture_pct": { "6in": 31.2, "12in": 40.1, "18in": 45.0 },
      "temp_c": { "6in": 24.3, "12in": 22.1, "18in": 20.8 },
      "battery_v": 4.01
    },
    {
      "node_id": "sim-01",
      "node_name": "Sim Node",
      "moisture_pct": { "6in": 25.4, "12in": 34.8, "18in": 41.9 },
      "battery_v": 3.9
    }
  ]
}
