{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Reachability Record Public API",
    "version": "1.0.0",
    "description": "Read-only observations from one monitor checking downdetector.com. A status verdict is current only when monitor.trusted is true and monitor.resultFreshness is current. This API is independent and is not an official Downdetector or Ookla service.",
    "termsOfService": "/terms-of-service",
    "contact": {
      "name": "Hasher Technologies LLC",
      "url": "/contact"
    },
    "license": {
      "name": "API access subject to Terms of Service",
      "identifier": "LicenseRef-Terms-of-Service"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "Current site origin"
    }
  ],
  "tags": [
    {
      "name": "Live observation",
      "description": "Latest monitor verdict and its trust metadata."
    },
    {
      "name": "Evidence",
      "description": "Stored history, probes, and incident records."
    },
    {
      "name": "Operations",
      "description": "Service health and release metadata."
    }
  ],
  "paths": {
    "/api/v1/status": {
      "get": {
        "operationId": "getCurrentStatus",
        "summary": "Read the latest monitor verdict",
        "description": "Returns the latest cached observation. Consumers must inspect monitor.trusted and monitor.resultFreshness before treating the verdict as current.",
        "tags": ["Live observation"],
        "security": [{}, { "PublicApiKey": [] }],
        "responses": {
          "200": {
            "description": "Latest observation, which can be current or explicitly untrusted.",
            "headers": {
              "Cache-Control": { "schema": { "type": "string" } },
              "RateLimit-Limit": { "schema": { "type": "integer" } },
              "RateLimit-Remaining": { "schema": { "type": "integer" } }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PublicStatus" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/InvalidApiKey" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/history": {
      "get": {
        "operationId": "getObservationHistory",
        "summary": "Read aggregated observation history",
        "tags": ["Evidence"],
        "security": [{}, { "PublicApiKey": [] }],
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "description": "Requested lookback in hours, bounded by the current retention allowance.",
            "schema": { "type": "integer", "minimum": 1 }
          },
          {
            "name": "binMinutes",
            "in": "query",
            "description": "Aggregation window in minutes, bounded from 5 minutes through 24 hours.",
            "schema": { "type": "integer", "minimum": 5, "maximum": 1440 }
          }
        ],
        "responses": {
          "200": {
            "description": "Aggregated checks over the aligned time window.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/History" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/InvalidApiKey" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/history/probes": {
      "get": {
        "operationId": "getProbeHistory",
        "summary": "Read recent probe-level evidence",
        "tags": ["Evidence"],
        "security": [{}, { "PublicApiKey": [] }],
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 200 }
          },
          {
            "name": "region",
            "in": "query",
            "description": "Opaque region alias returned by this endpoint, such as probe-1. Internal region labels are never returned.",
            "schema": { "type": "string", "pattern": "^probe-[1-9][0-9]*$" }
          },
          {
            "name": "checkType",
            "in": "query",
            "schema": { "type": "string", "enum": ["http", "tcp", "dns"] }
          },
          {
            "name": "ok",
            "in": "query",
            "schema": { "type": "boolean" }
          }
        ],
        "responses": {
          "200": {
            "description": "Recent snapshots and their matching probe observations.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ProbeHistory" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/InvalidApiKey" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/incidents": {
      "get": {
        "operationId": "listIncidentRecords",
        "summary": "List recent incident records",
        "description": "Incident records are stored events, not the current live verdict.",
        "tags": ["Evidence"],
        "security": [{}, { "PublicApiKey": [] }],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Incident records ordered newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["incidents"],
                  "properties": {
                    "incidents": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/IncidentSummary" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/InvalidApiKey" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/incidents/{id}": {
      "get": {
        "operationId": "getIncidentRecord",
        "summary": "Read one incident record",
        "tags": ["Evidence"],
        "security": [{}, { "PublicApiKey": [] }],
        "parameters": [{ "$ref": "#/components/parameters/IncidentId" }],
        "responses": {
          "200": {
            "description": "The stored incident record.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["incident"],
                  "properties": {
                    "incident": { "$ref": "#/components/schemas/IncidentDetail" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "401": { "$ref": "#/components/responses/InvalidApiKey" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/incidents/{id}/updates": {
      "get": {
        "operationId": "listIncidentUpdates",
        "summary": "Read one incident record's published timeline updates",
        "tags": ["Evidence"],
        "security": [{}, { "PublicApiKey": [] }],
        "parameters": [{ "$ref": "#/components/parameters/IncidentId" }],
        "responses": {
          "200": {
            "description": "Published updates in chronological order.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["updates"],
                  "properties": {
                    "updates": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/IncidentUpdate" }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/InvalidRequest" },
          "401": { "$ref": "#/components/responses/InvalidApiKey" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "getProcessHealth",
        "summary": "Read process liveness",
        "description": "A liveness check only. It does not prove that MongoDB or the monitor is ready.",
        "tags": ["Operations"],
        "responses": {
          "200": {
            "description": "The API process is responding.",
            "headers": {
              "Cache-Control": { "schema": { "type": "string", "const": "no-store" } }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProcessHealth" } } }
          }
        }
      }
    },
    "/api/v1/ready": {
      "get": {
        "operationId": "getServiceReadiness",
        "summary": "Read dependency and monitor readiness",
        "tags": ["Operations"],
        "responses": {
          "200": {
            "description": "Required dependencies and persisted monitor evidence are ready.",
            "headers": {
              "Cache-Control": { "schema": { "type": "string", "const": "no-store" } }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadinessReady" } } }
          },
          "503": {
            "description": "At least one required dependency or monitor-readiness check failed.",
            "headers": {
              "Cache-Control": { "schema": { "type": "string", "const": "no-store" } }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/ReadinessMongoUnavailable" },
                    { "$ref": "#/components/schemas/ReadinessMonitorUnavailable" }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/version": {
      "get": {
        "operationId": "getReleaseVersion",
        "summary": "Read deployed build metadata",
        "tags": ["Operations"],
        "responses": {
          "200": {
            "description": "Current build revision and build time.",
            "headers": {
              "Cache-Control": { "schema": { "type": "string", "const": "no-store" } }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReleaseVersion" } } }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PublicApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Optional account API key for a separate per-key rate identity. It does not promise a higher default limit. Public reads need no key, but a presented invalid key returns 401."
      }
    },
    "parameters": {
      "IncidentId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "MongoDB ObjectId for a public incident record.",
        "schema": { "type": "string", "pattern": "^[a-fA-F0-9]{24}$" }
      }
    },
    "responses": {
      "InvalidRequest": {
        "description": "The request parameters are invalid.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "The requested incident record does not exist for the configured target.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "InvalidApiKey": {
        "description": "An API key was presented but was invalid or revoked.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "The public-read rate limit was exceeded.",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" } }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "ServerError": {
        "description": "The service could not complete the read.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": ["string", "object"] },
          "requestId": { "type": ["string", "null"] }
        }
      },
      "MonitorAlert": {
        "type": ["object", "null"],
        "required": ["code", "level", "message", "observedAt"],
        "additionalProperties": false,
        "properties": {
          "code": { "type": "string" },
          "level": { "type": "string", "enum": ["warning", "critical"] },
          "message": { "type": "string" },
          "observedAt": { "type": "integer", "format": "int64" }
        }
      },
      "MonitorTrust": {
        "type": "object",
        "required": ["trusted", "resultFreshness", "running", "stale", "staleAfterMs", "lastRunCompletedAt", "alert"],
        "additionalProperties": false,
        "properties": {
          "trusted": { "type": "boolean" },
          "resultFreshness": { "type": "string", "enum": ["current", "stale", "unknown"] },
          "running": { "type": "boolean" },
          "stale": { "type": "boolean" },
          "staleAfterMs": { "type": "integer", "format": "int64" },
          "lastRunCompletedAt": { "type": ["integer", "null"], "format": "int64" },
          "alert": { "$ref": "#/components/schemas/MonitorAlert" }
        }
      },
      "PublicStatus": {
        "type": "object",
        "required": [
          "target",
          "status",
          "checkedAt",
          "lastCheckAt",
          "httpStatus",
          "latencyMs",
          "error",
          "intervalMs",
          "nextCheckAt",
          "incident",
          "monitor"
        ],
        "properties": {
          "target": { "type": "string", "format": "uri" },
          "status": { "type": "string", "enum": ["up", "down", "checking"] },
          "checkedAt": { "type": ["string", "null"], "format": "date-time" },
          "lastCheckAt": { "type": ["integer", "null"], "format": "int64" },
          "intervalMs": { "type": "integer", "minimum": 1 },
          "nextCheckAt": { "type": ["integer", "null"], "format": "int64" },
          "httpStatus": { "type": ["integer", "null"] },
          "latencyMs": { "type": ["integer", "null"] },
          "error": {
            "type": ["string", "null"],
            "description": "Fixed public failure summary. Raw probe diagnostics are not returned by this endpoint."
          },
          "incident": {
            "oneOf": [
              { "$ref": "#/components/schemas/IncidentStatus" },
              { "type": "null" }
            ]
          },
          "monitor": { "$ref": "#/components/schemas/MonitorTrust" }
        }
      },
      "ProcessHealth": {
        "type": "object",
        "required": ["status", "service", "uptimeSeconds"],
        "additionalProperties": false,
        "properties": {
          "status": { "type": "string", "const": "ok" },
          "service": { "type": "string", "const": "api" },
          "uptimeSeconds": { "type": "integer", "minimum": 0 }
        }
      },
      "ReleaseVersion": {
        "type": "object",
        "required": ["sha", "buildTime"],
        "additionalProperties": false,
        "properties": {
          "sha": { "type": "string" },
          "buildTime": { "type": "string" }
        }
      },
      "ReadinessReady": {
        "type": "object",
        "required": ["status", "service", "checks", "monitor"],
        "additionalProperties": false,
        "properties": {
          "status": { "type": "string", "const": "ready" },
          "service": { "type": "string", "const": "api" },
          "checks": {
            "type": "object",
            "required": ["mongodb", "monitor"],
            "additionalProperties": false,
            "properties": {
              "mongodb": { "type": "string", "const": "up" },
              "monitor": { "type": "string", "const": "up" }
            }
          },
          "monitor": {
            "type": "object",
            "required": ["lastRunCompletedAt", "intervalMs", "staleAfterMs", "alert"],
            "additionalProperties": false,
            "properties": {
              "lastRunCompletedAt": { "type": ["integer", "null"], "format": "int64" },
              "intervalMs": { "type": "integer", "minimum": 1 },
              "staleAfterMs": { "type": "integer", "minimum": 1 },
              "alert": { "$ref": "#/components/schemas/MonitorAlert" }
            }
          }
        }
      },
      "ReadinessMongoUnavailable": {
        "type": "object",
        "required": ["status", "service", "checks"],
        "additionalProperties": false,
        "properties": {
          "status": { "type": "string", "const": "not_ready" },
          "service": { "type": "string", "const": "api" },
          "checks": {
            "type": "object",
            "required": ["mongodb", "monitor"],
            "additionalProperties": false,
            "properties": {
              "mongodb": { "type": "string", "const": "down" },
              "monitor": { "type": "string", "const": "unknown" }
            }
          }
        }
      },
      "ReadinessMonitorUnavailable": {
        "type": "object",
        "required": ["status", "service", "checks", "monitor"],
        "additionalProperties": false,
        "properties": {
          "status": { "type": "string", "const": "not_ready" },
          "service": { "type": "string", "const": "api" },
          "checks": {
            "type": "object",
            "required": ["mongodb", "monitor"],
            "additionalProperties": false,
            "properties": {
              "mongodb": { "type": "string", "const": "up" },
              "monitor": { "type": "string", "const": "down" }
            }
          },
          "monitor": { "$ref": "#/components/schemas/NotReadyMonitor" }
        }
      },
      "NotReadyMonitor": {
        "type": "object",
        "required": ["running", "stale", "alert", "lastRunCompletedAt", "lastSuccessfulRunAt", "lastInternalError", "lastPersistenceError"],
        "additionalProperties": false,
        "properties": {
          "running": { "type": "boolean" },
          "stale": { "type": "boolean" },
          "alert": { "$ref": "#/components/schemas/MonitorAlert" },
          "lastRunCompletedAt": { "type": ["integer", "null"], "format": "int64" },
          "lastSuccessfulRunAt": { "type": ["integer", "null"], "format": "int64" },
          "lastInternalError": { "type": ["string", "null"] },
          "lastPersistenceError": { "type": ["string", "null"] }
        }
      },
      "ProbeResult": {
        "type": "object",
        "required": ["region", "checkType", "ok", "latencyMs", "detail"],
        "properties": {
          "region": {
            "type": "string",
            "pattern": "^probe-[1-9][0-9]*$",
            "description": "Opaque probe alias; configured region or host labels are not public."
          },
          "checkType": { "type": "string", "enum": ["http", "tcp", "dns"] },
          "ok": { "type": "boolean" },
          "latencyMs": { "type": ["number", "null"] },
          "detail": {
            "type": ["string", "null"],
            "description": "Sanitized outcome: Probe passed, Probe failed, Probe timed out, or a bare HTTP status."
          }
        }
      },
      "IncidentStatus": {
        "type": "object",
        "required": ["id", "state", "startedAt", "endedAt"],
        "properties": {
          "id": { "type": "string" },
          "state": { "type": "string", "enum": ["open", "resolved"] },
          "startedAt": { "type": "string", "format": "date-time" },
          "endedAt": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "HistoryBin": {
        "type": "object",
        "required": ["start", "up", "down", "total", "status"],
        "properties": {
          "start": { "type": "string", "format": "date-time" },
          "up": { "type": "integer" },
          "down": { "type": "integer" },
          "total": { "type": "integer" },
          "status": {
            "type": "string",
            "enum": ["up", "down", "empty"],
            "description": "A period is down when down > 0 and down >= up; otherwise it is up. A period with no samples is unknown."
          }
        }
      },
      "History": {
        "type": "object",
        "required": ["target", "retentionDays", "maxHours", "from", "to", "binMinutes", "samples", "uptime", "bins"],
        "properties": {
          "target": { "type": "string", "format": "uri" },
          "retentionDays": { "type": "integer" },
          "maxHours": { "type": "integer" },
          "from": { "type": "string", "format": "date-time" },
          "to": { "type": "string", "format": "date-time" },
          "binMinutes": { "type": "integer" },
          "samples": {
            "type": "integer",
            "description": "Count of observed samples in the response window; it does not imply continuous or expected coverage."
          },
          "uptime": {
            "type": "object",
            "description": "Legacy field name for successful-check counts and their share among observed samples; it is not a continuous-availability claim.",
            "required": ["up", "down", "total", "pct"],
            "properties": {
              "up": { "type": "integer" },
              "down": { "type": "integer" },
              "total": { "type": "integer" },
              "pct": { "type": ["number", "null"] }
            }
          },
          "bins": { "type": "array", "items": { "$ref": "#/components/schemas/HistoryBin" } }
        }
      },
      "ProbeHistory": {
        "type": "object",
        "required": ["target", "retentionDays", "maxHours", "from", "to", "filters", "count", "snapshots"],
        "properties": {
          "target": { "type": "string", "format": "uri" },
          "retentionDays": { "type": "integer" },
          "maxHours": { "type": "integer" },
          "from": { "type": "string", "format": "date-time" },
          "to": { "type": "string", "format": "date-time" },
          "filters": {
            "type": "object",
            "required": ["region", "checkType", "ok"],
            "properties": {
              "region": { "type": ["string", "null"], "pattern": "^probe-[1-9][0-9]*$" },
              "checkType": { "type": ["string", "null"], "enum": ["http", "tcp", "dns", null] },
              "ok": { "type": ["boolean", "null"] }
            }
          },
          "count": { "type": "integer" },
          "snapshots": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["checkedAt", "status", "assertionSummary", "probeResults"],
              "properties": {
                "checkedAt": { "type": "string", "format": "date-time" },
                "status": { "type": "string", "enum": ["up", "down"] },
                "assertionSummary": { "type": ["string", "null"] },
                "probeResults": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/ProbeResult" }
                }
              }
            }
          }
        }
      },
      "IncidentSummary": {
        "type": "object",
        "required": ["id", "target", "state", "startedAt", "endedAt", "trigger", "falsePositive", "falsePositiveReason"],
        "properties": {
          "id": { "type": "string" },
          "target": { "type": "string", "format": "uri" },
          "state": { "type": "string", "enum": ["open", "resolved"] },
          "startedAt": { "type": "string", "format": "date-time" },
          "endedAt": { "type": ["string", "null"], "format": "date-time" },
          "trigger": { "type": ["string", "null"] },
          "falsePositive": { "type": "boolean" },
          "falsePositiveReason": { "type": ["string", "null"] }
        }
      },
      "IncidentDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/IncidentSummary" },
          {
            "type": "object",
            "required": ["falsePositiveMarkedAt", "falsePositiveMarkedBy", "resolutionNote", "resolvedBy"],
            "properties": {
              "falsePositiveMarkedAt": { "type": ["string", "null"], "format": "date-time" },
              "falsePositiveMarkedBy": { "type": ["string", "null"], "description": "Privacy-preserving label: Monitor operator when present." },
              "resolutionNote": { "type": ["string", "null"], "description": "Operator-authored note stored atomically with manual resolution when supplied." },
              "resolvedBy": { "type": ["string", "null"], "description": "Privacy-preserving label: Monitor operator when a resolution note has an author." }
            }
          }
        ]
      },
      "IncidentUpdate": {
        "type": "object",
        "required": ["id", "kind", "body", "createdBy", "createdAt"],
        "properties": {
          "id": { "type": "string" },
          "kind": { "type": "string", "enum": ["update", "postmortem"] },
          "body": { "type": "string" },
          "createdBy": { "type": ["string", "null"], "description": "Privacy-preserving label: Monitor operator when present." },
          "createdAt": { "type": ["string", "null"], "format": "date-time" }
        }
      }
    }
  }
}
