{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://tickwire.io/schemas/status-feed-v1.json",
  "title": "Tickwire public status feed (tickwire.status.v1)",
  "type": "object",
  "required": ["schema", "generated_at", "page", "status", "components", "groups", "regions",
               "account_classes", "incidents", "maintenances", "postmortems", "subscribe"],
  "additionalProperties": false,
  "$defs": {
    "time": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$"},
    "timeOrNull": {"oneOf": [{"$ref": "#/$defs/time"}, {"type": "null"}]},
    "pctOrNull": {"oneOf": [{"type": "number", "minimum": 0, "maximum": 100}, {"type": "null"}]},
    "componentStatus": {"enum": ["operational", "degraded_performance", "partial_outage", "major_outage",
                                 "under_maintenance", "unknown"]},
    "rollup": {
      "type": "object",
      "required": ["name", "status", "components"],
      "additionalProperties": false,
      "properties": {
        "name": {"type": "string"},
        "status": {"$ref": "#/$defs/componentStatus"},
        "components": {"type": "array", "items": {"type": "string"}}
      }
    },
    "incident": {
      "type": "object",
      "required": ["id", "title", "status", "impact", "automatic", "components", "started_at", "resolved_at",
                   "scheduled_for", "scheduled_until", "updates", "url", "postmortem"],
      "additionalProperties": false,
      "properties": {
        "id": {"type": "integer"},
        "url": {"type": ["string", "null"], "description": "The human incident page (null if no site URL is set)."},
        "postmortem": {
          "description": "Null until a postmortem is published. Drafts are never shown.",
          "oneOf": [
            {"type": "null"},
            {"type": "object", "required": ["published_at", "updated_at"], "additionalProperties": false,
             "properties": {"published_at": {"$ref": "#/$defs/time"}, "updated_at": {"$ref": "#/$defs/time"},
                            "body": {"type": "string"}}}
          ]
        },
        "title": {"type": "string"},
        "status": {"enum": ["investigating", "identified", "monitoring", "resolved", "scheduled", "in_progress",
                            "completed"]},
        "impact": {"enum": ["none", "minor", "major", "critical", "maintenance"]},
        "automatic": {"type": "boolean", "description": "Opened by the probe engine rather than a person."},
        "components": {
          "type": "array",
          "items": {
            "type": "object",
            "required": ["id", "name", "status"],
            "additionalProperties": false,
            "properties": {"id": {"type": "string"}, "name": {"type": "string"},
                           "status": {"$ref": "#/$defs/componentStatus"}}
          }
        },
        "started_at": {"$ref": "#/$defs/time"},
        "resolved_at": {"$ref": "#/$defs/timeOrNull"},
        "scheduled_for": {"$ref": "#/$defs/timeOrNull"},
        "scheduled_until": {"$ref": "#/$defs/timeOrNull"},
        "updates": {
          "type": "array",
          "description": "Newest first.",
          "items": {
            "type": "object",
            "required": ["at", "status", "body"],
            "additionalProperties": false,
            "properties": {"at": {"$ref": "#/$defs/time"}, "status": {"type": "string"}, "body": {"type": "string"}}
          }
        }
      }
    }
  },
  "properties": {
    "schema": {"const": "tickwire.status.v1"},
    "generated_at": {"$ref": "#/$defs/time"},
    "page": {
      "type": "object",
      "required": ["name", "url"],
      "properties": {"name": {"type": "string"}, "url": {"type": "string"}}
    },
    "status": {
      "type": "object",
      "required": ["indicator", "description"],
      "additionalProperties": false,
      "properties": {
        "indicator": {"enum": ["none", "minor", "major", "critical", "maintenance", "unknown"]},
        "description": {"type": "string"}
      }
    },
    "components": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["id", "name", "group", "description", "region", "account_class", "status",
                     "measured_status", "updated_at", "probes", "uptime"],
        "additionalProperties": false,
        "properties": {
          "id": {"type": "string"},
          "name": {"type": "string"},
          "group": {"type": "string"},
          "description": {"type": "string"},
          "region": {"type": ["string", "null"]},
          "account_class": {"type": ["string", "null"]},
          "status": {"$ref": "#/$defs/componentStatus"},
          "measured_status": {"$ref": "#/$defs/componentStatus",
                              "description": "What the probes alone say, before incidents are applied."},
          "in_maintenance": {"type": "boolean",
                             "description": "An active maintenance window covers this component. Maintenance never hides a major outage or unknown: status keeps those and this flag adds the note."},
          "updated_at": {"$ref": "#/$defs/timeOrNull"},
          "probes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "name", "kind", "region", "account_class", "state", "fresh", "checked_at",
                           "latency_ms", "detail"],
              "additionalProperties": false,
              "properties": {
                "id": {"type": "string"},
                "name": {"type": "string"},
                "kind": {"enum": ["http", "bridge_health", "tcp", "webhook_receipt"]},
                "region": {"type": ["string", "null"]},
                "account_class": {"type": ["string", "null"]},
                "state": {"enum": ["up", "degraded", "down", "unknown"]},
                "fresh": {"type": "boolean"},
                "checked_at": {"$ref": "#/$defs/timeOrNull"},
                "latency_ms": {"type": ["number", "null"]},
                "detail": {"type": "string"}
              }
            }
          },
          "uptime": {
            "type": "object",
            "required": ["24h", "7d", "30d", "90d"],
            "additionalProperties": false,
            "properties": {
              "24h": {"$ref": "#/$defs/pctOrNull"},
              "7d": {"$ref": "#/$defs/pctOrNull"},
              "30d": {"$ref": "#/$defs/pctOrNull"},
              "90d": {"$ref": "#/$defs/pctOrNull"}
            }
          },
          "history": {
            "type": "array",
            "description": "Daily buckets, oldest first, ending today (UTC). Omitted when history_days=0.",
            "items": {
              "type": "object",
              "required": ["date", "uptime_pct", "status", "samples", "avg_latency_ms"],
              "additionalProperties": false,
              "properties": {
                "date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"},
                "uptime_pct": {"$ref": "#/$defs/pctOrNull"},
                "status": {"enum": ["operational", "degraded_performance", "partial_outage", "major_outage",
                                    "no_data"]},
                "samples": {
                  "type": "object",
                  "required": ["up", "degraded", "down"],
                  "additionalProperties": false,
                  "properties": {"up": {"type": "integer", "minimum": 0},
                                 "degraded": {"type": "integer", "minimum": 0},
                                 "down": {"type": "integer", "minimum": 0},
                                 "paused": {"type": "integer", "minimum": 0,
                                            "description": "Down results during a planned sleep; left out of uptime_pct."}}
                },
                "avg_latency_ms": {"type": ["number", "null"]}
              }
            }
          }
        }
      }
    },
    "not_monitored": {
      "type": "array",
      "description": "Optional (older feeds lack it). Components with no probe configured yet and no human-declared status: no evidence either way, so they are not in components, the rollups or the headline. Show them as 'Not monitored yet', never as up or down.",
      "items": {
        "type": "object",
        "required": ["id", "name", "group", "description"],
        "additionalProperties": false,
        "properties": {
          "id": {"type": "string"},
          "name": {"type": "string"},
          "group": {"type": ["string", "null"]},
          "description": {"type": ["string", "null"]}
        }
      }
    },
    "groups": {"type": "array", "items": {"$ref": "#/$defs/rollup"}},
    "regions": {"type": "array", "items": {"$ref": "#/$defs/rollup"}},
    "account_classes": {"type": "array", "items": {"$ref": "#/$defs/rollup"}},
    "incidents": {
      "type": "object",
      "required": ["active", "recent"],
      "additionalProperties": false,
      "properties": {
        "active": {"type": "array", "items": {"$ref": "#/$defs/incident"}},
        "recent": {"type": "array", "items": {"$ref": "#/$defs/incident"}}
      }
    },
    "maintenances": {
      "type": "object",
      "required": ["active", "upcoming"],
      "additionalProperties": false,
      "properties": {
        "active": {"type": "array", "items": {"$ref": "#/$defs/incident"}},
        "upcoming": {"type": "array", "items": {"$ref": "#/$defs/incident"}}
      }
    },
    "postmortems": {
      "type": "array",
      "description": "Published postmortems, newest first. Empty until one is published; none are seeded.",
      "items": {
        "type": "object",
        "required": ["incident_id", "title", "impact", "started_at", "resolved_at", "published_at", "url"],
        "additionalProperties": false,
        "properties": {
          "incident_id": {"type": "integer"},
          "title": {"type": "string"},
          "impact": {"enum": ["none", "minor", "major", "critical", "maintenance"]},
          "started_at": {"$ref": "#/$defs/time"},
          "resolved_at": {"$ref": "#/$defs/timeOrNull"},
          "published_at": {"$ref": "#/$defs/time"},
          "url": {"type": ["string", "null"]}
        }
      }
    },
    "subscribe": {
      "type": "object",
      "description": "Ways to follow updates right now. email is false unless an email provider is configured.",
      "required": ["email", "rss", "atom", "json"],
      "additionalProperties": false,
      "properties": {
        "email": {"type": "boolean"},
        "rss": {"type": "string"},
        "atom": {"type": "string"},
        "json": {"type": "string"}
      }
    },
    "power": {
      "type": "object",
      "description": "Optional (older feeds lack it). Whether the platform is paused on purpose: a planned sleep the gateway confirmed. paused is never true without that evidence.",
      "required": ["configured", "paused", "state", "since", "checked_at", "description"],
      "additionalProperties": false,
      "properties": {
        "configured": {"type": "boolean"},
        "paused": {"type": "boolean"},
        "state": {"type": ["string", "null"]},
        "since": {"$ref": "#/$defs/timeOrNull"},
        "checked_at": {"$ref": "#/$defs/timeOrNull"},
        "description": {"type": ["string", "null"]}
      }
    }
  }
}
