{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.healthexport.dev/schemas/webhook/v1/sleep-sessions.json",
  "title": "MetricBridge webhook: sleep.sessions",
  "description": "Every sleep session whose waking day falls in data.coverage.fromDay..toDay. Replace what you stored for exactly those days: sessions can split, merge or move when a source syncs late.",
  "allOf": [{ "$ref": "common.json#/$defs/typedDocument" }],
  "type": "object",
  "properties": {
    "type": { "const": "sleep.sessions" },
    "data": {
      "type": "object",
      "required": ["metrics", "coverage", "sleepSessions"],
      "properties": {
        "coverage": {
          "type": "object",
          "required": ["fromDay", "toDay", "mode"],
          "properties": {
            "fromDay": { "$ref": "common.json#/$defs/isoDay" },
            "toDay": { "$ref": "common.json#/$defs/isoDay" },
            "mode": { "const": "replace_days" }
          }
        },
        "sleepSessions": { "type": "array", "items": { "$ref": "#/$defs/session" } }
      }
    }
  },
  "$defs": {
    "session": {
      "type": "object",
      "required": ["id", "start", "end", "day", "hours", "nap", "main", "tz", "tzSource", "staged", "sources", "segments"],
      "properties": {
        "id": { "type": "string", "pattern": "^slp_\\d+$", "description": "slp_ + start as Unix seconds. Stable across re-sends while the start does not move." },
        "start": { "$ref": "common.json#/$defs/localDateTime" },
        "end": { "$ref": "common.json#/$defs/localDateTime" },
        "day": { "$ref": "common.json#/$defs/isoDay", "description": "Waking day (the day the session ends), the same day the daily sleep_analysis total is filed under." },
        "hours": { "type": "number", "minimum": 0, "description": "Time asleep, overlapping sources counted once." },
        "nap": { "type": "boolean" },
        "main": { "type": "boolean", "description": "The day's main sleep. Exactly one session per day is main." },
        "tz": { "$ref": "common.json#/$defs/ianaZone" },
        "tzSource": { "enum": ["sample", "device"], "description": "sample: from the recorded samples' time zone metadata. device: the phone's zone at export time." },
        "staged": { "type": "boolean", "description": "True when any segment has core, deep or REM stages." },
        "stages": {
          "type": "object",
          "description": "Hours per stage, summed across sources like the daily total's stage fields. Absent when no stage data.",
          "properties": {
            "core": { "type": "number" }, "deep": { "type": "number" },
            "rem": { "type": "number" }, "awake": { "type": "number" }
          }
        },
        "sources": { "type": "array", "items": { "type": "string" }, "description": "Bundle identifiers of every app or device that wrote samples in the session." },
        "segments": {
          "type": "array",
          "description": "Every Health sleep sample overlapping the session, as recorded (unclipped, all sources). Filter by source to avoid double counting.",
          "items": {
            "type": "object",
            "required": ["stage", "start", "end", "source"],
            "properties": {
              "stage": { "enum": ["core", "deep", "rem", "asleep", "awake", "in_bed"] },
              "start": { "$ref": "common.json#/$defs/localDateTime" },
              "end": { "$ref": "common.json#/$defs/localDateTime" },
              "source": { "type": "string" }
            }
          }
        }
      }
    }
  }
}
