{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:cede:schema:event-footprint:v0",
  "title": "CEDE event footprint, v0",
  "description": "One catalogue event's footprint as the pinned record has it: the body of GET /events/{peril}/{event_id}/footprint (SPEC.md section 3.2). The footprint published here is the occurrence — the catalogue solutions this build's own aggregation rule treats as one event with the one asked about, and the box they span. It is deliberately not a modelled ground-motion field: the pinned snapshot holds hypocentre solutions and no intensity surface, and synthesising one would be a model with assumptions and a version, which belongs in the registry with a card on it. `limitations` says so on every response. The feed id, the vintage and the sha256 of the exact bytes are named, and P10 holds as everywhere else on the feed surface — hazard data, never an individual-level exposure record.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schema_version",
    "peril",
    "event_id",
    "feed",
    "aggregation_level",
    "coverage",
    "event",
    "footprint",
    "limitations",
    "notes"
  ],
  "properties": {
    "schema_version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
    "peril": { "type": "string", "minLength": 1 },
    "event_id": {
      "type": "string",
      "minLength": 1,
      "description": "The publisher's own identifier for the solution asked about."
    },
    "feed": { "$ref": "#/$defs/data_source" },
    "aggregation_level": { "$ref": "#/$defs/aggregation_level" },
    "coverage": { "$ref": "#/$defs/coverage" },
    "event": { "$ref": "#/$defs/event" },
    "footprint": {
      "type": "object",
      "additionalProperties": false,
      "required": ["basis", "description", "extent", "occurrence"],
      "properties": {
        "basis": {
          "type": "string",
          "enum": ["catalogue_occurrence"],
          "description": "What kind of footprint this is. One value in this build: a cluster of catalogue solutions. A modelled intensity footprint would be a different basis, carried by a model with a card, and this enum is where that distinction stays visible."
        },
        "description": { "type": "string", "minLength": 1 },
        "extent": { "$ref": "#/$defs/box" },
        "occurrence": {
          "type": "object",
          "additionalProperties": false,
          "description": "The cluster: the aggregation rule applied, and every solution that fell inside it. This is the transformation a backtest applies between catalogue events and a payout, published so it can be reproduced.",
          "required": [
            "window",
            "window_measured_from",
            "minimum_magnitude",
            "from",
            "to",
            "event_count",
            "peak_event_id",
            "events"
          ],
          "properties": {
            "window": {
              "type": "string",
              "pattern": "^P(?!$)([0-9]+D)?(T(?!$)([0-9]+H)?([0-9]+M)?([0-9]+S)?)?$",
              "description": "The aggregation window as an ISO-8601 duration, measured from the first event of the cluster."
            },
            "window_measured_from": { "type": "string", "minLength": 1 },
            "minimum_magnitude": {
              "type": "number",
              "description": "The magnitude floor events had to clear to take part in the clustering — the request's, or the snapshot's own minimum when it named none."
            },
            "from": { "$ref": "#/$defs/timestamp" },
            "to": { "$ref": "#/$defs/timestamp" },
            "event_count": { "type": "integer", "minimum": 1 },
            "peak_event_id": {
              "type": "string",
              "minLength": 1,
              "description": "The largest magnitude in the cluster — the level an index reads for this occurrence."
            },
            "events": {
              "type": "array",
              "minItems": 1,
              "description": "Every solution in the cluster, in time order, each with its own position: the occurrence rule is time only, so members can be far apart and are never summarised into one point.",
              "items": { "$ref": "#/$defs/event" }
            }
          }
        }
      }
    },
    "limitations": {
      "type": "array",
      "minItems": 1,
      "description": "What this footprint is not. Required and non-empty: a footprint document that stated no limitation would read as a hazard surface, which this is not.",
      "items": { "type": "string", "minLength": 1 }
    },
    "notes": {
      "type": "array",
      "minItems": 1,
      "items": { "type": "string", "minLength": 1 }
    }
  },
  "$defs": {
    "timestamp": {
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?Z$"
    },
    "event": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "event_id",
        "time",
        "latitude",
        "longitude",
        "depth_km",
        "magnitude",
        "magnitude_type",
        "footprint"
      ],
      "properties": {
        "event_id": { "type": "string", "minLength": 1 },
        "time": { "$ref": "#/$defs/timestamp" },
        "latitude": { "type": "number", "minimum": -90, "maximum": 90 },
        "longitude": { "type": "number", "minimum": -180, "maximum": 180 },
        "depth_km": { "type": ["number", "null"] },
        "magnitude": { "type": "number" },
        "magnitude_type": { "type": ["string", "null"] },
        "footprint": { "type": "string", "minLength": 1 }
      }
    },
    "box": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "min_latitude",
        "max_latitude",
        "min_longitude",
        "max_longitude",
        "bounds"
      ],
      "properties": {
        "min_latitude": { "type": "number", "minimum": -90, "maximum": 90 },
        "max_latitude": { "type": "number", "minimum": -90, "maximum": 90 },
        "min_longitude": { "type": "number", "minimum": -180, "maximum": 180 },
        "max_longitude": { "type": "number", "minimum": -180, "maximum": 180 },
        "bounds": { "type": "string", "minLength": 1 }
      }
    },
    "aggregation_level": {
      "type": "object",
      "required": ["level", "p10"],
      "properties": {
        "level": { "type": "string", "minLength": 1 },
        "note": { "type": "string", "minLength": 1 },
        "p10": { "type": "string", "minLength": 1 }
      }
    },
    "coverage": {
      "type": "object",
      "required": ["period_of_record", "bounding_box", "minimum_magnitude", "statement"],
      "properties": {
        "period_of_record": {
          "type": "object",
          "required": ["start", "end"],
          "properties": {
            "start": { "type": "string", "minLength": 1 },
            "end": { "type": "string", "minLength": 1 },
            "years": { "type": "integer", "minimum": 0 }
          }
        },
        "bounding_box": {
          "type": "object",
          "required": [
            "min_latitude",
            "max_latitude",
            "min_longitude",
            "max_longitude"
          ],
          "properties": {
            "min_latitude": { "type": "number" },
            "max_latitude": { "type": "number" },
            "min_longitude": { "type": "number" },
            "max_longitude": { "type": "number" }
          }
        },
        "minimum_magnitude": { "type": "number" },
        "event_type": { "type": "string", "minLength": 1 },
        "magnitude_basis": { "type": "string", "minLength": 1 },
        "statement": { "type": "string", "minLength": 1 }
      }
    },
    "data_source": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "id",
        "name",
        "publisher",
        "kind",
        "version",
        "vintage",
        "sha256",
        "licence"
      ],
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "name": { "type": "string", "minLength": 1 },
        "publisher": { "type": "string", "minLength": 1 },
        "kind": {
          "type": "string",
          "enum": [
            "quake_catalogue",
            "storm_track_set",
            "station_network",
            "footprint_provider",
            "outage_feed",
            "other"
          ]
        },
        "version": { "type": "string", "minLength": 1 },
        "vintage": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
        "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
        "query_url": { "type": "string", "minLength": 1 },
        "record_count": { "type": "integer", "minimum": 0 },
        "licence": { "type": "string", "minLength": 1 }
      }
    }
  }
}
