{
  "version": "1.0.0",
  "events": {
    "camera.snapshot": {
      "type": "object",
      "required": [
        "image_url",
        "width",
        "height"
      ],
      "properties": {
        "image_url": {
          "type": "null"
        },
        "width": {
          "type": "integer",
          "minimum": 1,
          "maximum": 4096
        },
        "height": {
          "type": "integer",
          "minimum": 1,
          "maximum": 4096
        },
        "captured_at": {
          "type": "string",
          "maxLength": 40
        }
      }
    },
    "button.changed": {
      "type": "object",
      "required": [
        "pressed"
      ],
      "properties": {
        "pressed": {
          "type": "boolean"
        }
      }
    },
    "motion.sample": {
      "type": "object",
      "required": [
        "moving",
        "acceleration_g",
        "rotation_dps"
      ],
      "properties": {
        "moving": {
          "type": "boolean"
        },
        "acceleration_g": {
          "type": "object",
          "required": [
            "x",
            "y",
            "z"
          ],
          "properties": {
            "x": {
              "type": "number",
              "minimum": -16,
              "maximum": 16
            },
            "y": {
              "type": "number",
              "minimum": -16,
              "maximum": 16
            },
            "z": {
              "type": "number",
              "minimum": -16,
              "maximum": 16
            }
          }
        },
        "rotation_dps": {
          "type": "object",
          "required": [
            "x",
            "y",
            "z"
          ],
          "properties": {
            "x": {
              "type": "number",
              "minimum": -2000,
              "maximum": 2000
            },
            "y": {
              "type": "number",
              "minimum": -2000,
              "maximum": 2000
            },
            "z": {
              "type": "number",
              "minimum": -2000,
              "maximum": 2000
            }
          }
        }
      }
    },
    "distance.sample": {
      "type": "object",
      "required": [
        "distance_mm",
        "valid"
      ],
      "properties": {
        "distance_mm": {
          "type": "integer",
          "minimum": 0,
          "maximum": 2000
        },
        "valid": {
          "type": "boolean"
        }
      }
    },
    "climate.sample": {
      "type": "object",
      "required": [
        "temperature_c",
        "humidity_pct"
      ],
      "properties": {
        "temperature_c": {
          "type": "number",
          "minimum": -40,
          "maximum": 125
        },
        "humidity_pct": {
          "type": "number",
          "minimum": 0,
          "maximum": 100
        }
      }
    },
    "force.sample": {
      "type": "object",
      "required": [
        "raw",
        "pressed"
      ],
      "properties": {
        "raw": {
          "type": "integer",
          "minimum": 0,
          "maximum": 4095
        },
        "pressed": {
          "type": "boolean"
        }
      }
    }
  },
  "app": {
    "title": "My hardware app",
    "widgets": [
      {
        "type": "metric",
        "label": "Temperature",
        "device_id": "dev_REPLACE_WITH_YOUR_DEVICE_ID",
        "event_type": "climate.sample",
        "field": "temperature_c",
        "unit": "°C"
      }
    ]
  },
  "notes": [
    "All sensor data is explicitly physical or simulation. Sample data never becomes a physical reading.",
    "Widget types: metric, status, json, camera. Fixed renderer, plain-text labels, no scripts or HTML.",
    "Camera Kit may POST image/jpeg (maximum 256 KiB, dimensions up to 4096) to /api/v1/snapshot with its device token. Only the latest private image is kept. Owners fetch /api/v1/devices/{id}/image with workspace authentication. image_url is always null; no public image links.",
    "Devices send via HTTPS. Bluetooth commissioning and native iOS/Android apps are not released."
  ]
}