{
  "openapi": "3.1.0",
  "info": {
    "title": "SanAlert API",
    "version": "1",
    "summary": "Public, read-only: levels of threat from the air for places in Poland, the official RCB and RSO messages, and what they rest on.",
    "description": "SanAlert (sanalert.pl) is an unofficial, free public service by Applied AI sp. z o.o. (Przemyśl, Poland). It shows whether drones or missiles tracked over Ukraine are heading for a place in Poland, as one of four levels (`quiet`, `activity`, `approaching`, `near`), and shows the official Polish RCB and RSO messages separately and unchanged.\n\nEvery endpoint here is read-only and public: no key, no account, no cookies. Each document is the same for everyone; a client picks its own place out of it on the device. This document describes version 1 of the documents; the running server's version is `version` in `/v1/health`. The JSON Schemas of the state, history, official-messages and source-card documents are under `components/schemas` exactly as SanAlert's contract publishes them (`state.v1`, `history.v1`, `official.v1`, `sources.v1`).\n\nFor AI assistants, `POST /mcp` is a read-only MCP server (Model Context Protocol, the streamable HTTP transport, stateless, with JSON answers) over the same documents, for the whole of Poland: the state summarised, the official messages of the last 24 hours, the night reports, the source cards and the reliability record. None of its tools takes a place. Every result carries the state's `notice`, `attribution` and these conditions, to be passed on. It is a JSON-RPC transport, not a document, so it is not listed under `paths`; the same conditions bind it.\n\n## Conditions of use\n\nAnyone who reads this API, and any service that shows its data, keeps these conditions.\n\n1. **Unofficial, and said so.** SanAlert is not an official warning system or a government channel. It does not replace sirens, RCB alerts or instructions from the authorities. Information comes from open sources and may be incomplete or delayed. A service that shows this data carries that safety notice and always refers its readers to the official air-raid alarms: sirens, Alert RCB and the authorities' instructions (NEPTUN's API terms, §5, bind every service built on its data).\n2. **`notice` and `attribution` pass through.** Show the state document's `notice` (in the reader's language: `pl`, `en` or `uk`) on every screen that shows a level, and a visible link to every entry in `attribution`. Do not drop, shorten or hide them, and do not present the data as your own.\n3. **NEPTUN's link.** The data includes NEPTUN data (the objects tracked over Ukraine and the air-raid alarms in its districts). Keep a visible link to NEPTUN, https://neptun.in.ua, next to the map or the data, for example with the text `Дані: Карта повітряних тривог — NEPTUN` (NEPTUN's API terms, https://neptun.in.ua/api-terms, §3). NEPTUN's name and logo do not pass with the data (§7): do not present your service as NEPTUN's.\n4. **No data is not quiet.** When `levels_valid` is `false`, or `health.status` is `unknown`, or `generated_at` is more than 60 seconds old, say \"no data\" everywhere, never \"quiet\".\n5. **Poll politely.** Fetch `/v1/state` at most once every 5 seconds, with `If-None-Match` (the answer is `304` with no body while nothing changed), and not while nothing is on screen. The answers are cached by a CDN: do not add cache busters. The other documents give their own pace below.\n6. **Nothing about the reader.** A request must not identify the reader: no place, no account, no e-mail address or other personal data in the URL, the headers or the User-Agent. A User-Agent may name the application that asks (`MyApp/1.2`) and nothing else.\n7. **Licences of the parts.** `aircraft` in the state document is © adsb.lol contributors, ODbL 1.0: whoever republishes it does so under ODbL. The raion shapes of Ukraine are CC BY 3.0 IGO (OCHA, SSPE Kartographia, via geoBoundaries), the shelter register CC BY 4.0 (KG PSP, dane.gov.pl), the powiat boundaries CC BY 4.0 (GUGiK). Each is an entry in `attribution`, and `/v1/sources` says what every source is and what it cannot see.",
    "termsOfService": "https://sanalert.pl/en/terms/",
    "contact": {
      "name": "Applied AI sp. z o.o.",
      "url": "https://sanalert.pl/en/",
      "email": "kontakt@appliedai.dev"
    }
  },
  "externalDocs": {
    "description": "How SanAlert works: the sources, the levels, what it does not do",
    "url": "https://sanalert.pl/en/how-it-works/"
  },
  "servers": [
    {
      "url": "https://api.sanalert.pl"
    }
  ],
  "paths": {
    "/v1/state": {
      "get": {
        "operationId": "getState",
        "summary": "The live state: levels by grid cell and voivodeship, tracked objects, alarms in Ukraine, official messages",
        "description": "The one document every SanAlert client reads, rebuilt about every 2 seconds: the level of every raised cell of Poland's grid (`cells`, keyed by the cell id of `/v1/grid/1.json`; a cell not listed is quiet, but only while condition 4 allows levels at all), the level of each voivodeship (`regions`), the objects NEPTUN tracks (`objects`), the air-raid alarms in Ukraine (`ua_alarms`, never a SanAlert level), military aircraft that broadcast their position (`aircraft`, display only: they never change a level), the official RCB and RSO messages (`official`, shown separately and unchanged, never a level), the gamma dose rate readings of Państwowa Agencja Atomistyki above PAA's own line of 0.3 µSv/h (`radiation`, shown even with the radiation layer off, display only, never a level; null while PAA's readings are unavailable), the health of the sources (`health`), `notice` and `attribution`. Levels come from the cells: never recompute them.\n\n**Conditions of use** (in full at the top of this document):\n\n- SanAlert is unofficial: it does not replace sirens, RCB alerts or official instructions. Show `notice` in the reader's language on every screen that shows a level, and always refer readers to the official air-raid alarms (NEPTUN's API terms, §5).\n- The data includes NEPTUN data: keep a visible link to https://neptun.in.ua next to the data, for example with the text `Дані: Карта повітряних тривог — NEPTUN`, and a visible link to every other entry in `attribution`. Pass `notice` and `attribution` through unchanged.\n- No data is not quiet: when `levels_valid` is `false`, `health.status` is `unknown`, or `generated_at` is more than 60 seconds old, say \"no data\", never \"quiet\".\n- Poll at most once every 5 seconds, with `If-None-Match`, and not while nothing is on screen; no cache busters.\n- Nothing in a request identifies the reader: no place and no personal data in the URL or the headers. Find the reader's cell on the device.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The state document.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=2, s-maxage=4, stale-while-revalidate=30"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/state.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "503": {
            "description": "Not ready: the server has not built its first state yet (a second or two after a start).",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "not_ready"
                }
              }
            }
          }
        }
      }
    },
    "/v1/compact": {
      "get": {
        "operationId": "getCompact",
        "summary": "The live state in a few kilobytes, for watches",
        "description": "Built in the same cycle from the same document as `/v1/state`, so the two never disagree: every raised cell as runs along its grid row (`cells`: row `i` → `[[first j, last j, level]]`, where `1` is activity, `2` approaching, `3` near; a cell in no run is quiet), the voivodeships above quiet (`regions`), the official messages in force, strongest first (`official.now`; `official.available: false` means RCB or RSO could not be read: say so, never \"no alerts\"), and the state's `notice` and `attribution`, word for word. Every condition of `/v1/state` binds it, the pace included.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The compact state.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=2, s-maxage=4, stale-while-revalidate=30"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/compact.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "503": {
            "description": "Not ready: the server has not built its first state yet (a second or two after a start).",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "not_ready"
                }
              }
            }
          }
        }
      }
    },
    "/v1/history": {
      "get": {
        "operationId": "getHistory",
        "summary": "What happened at every cell over the last twelve hours",
        "description": "Runs of one level at every cell that was above quiet (`cells`), the stretches in which levels were not valid (`gaps`: nothing is known about any cell inside a gap, neither quiet nor raised) and the official messages the state carried in the window (`official`, with `seen_from` and `seen_to`). Context, never the current level: the level at a place is the state's. Before `covers_from` nothing is known (the server keeps this in memory, and a restart begins it again). A document more than 5 minutes old is not shown. Fetch it when it is shown, then at most once a minute, with `If-None-Match`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The history document.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=30, s-maxage=30"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/history.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "503": {
            "description": "No history yet: the server has not had its first valid state since it started.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_history"
                }
              }
            }
          }
        }
      }
    },
    "/v1/official": {
      "get": {
        "operationId": "getOfficial",
        "summary": "The official RCB and RSO messages of the last 24 hours",
        "description": "Every RCB and RSO message the server saw in the last 24 hours, with when it first appeared (`seen_from`, `null` when it was already there when the server first looked) and when it left (`seen_to`, `null` while it is still there). `cancelled_seen` is when a cancelled RCB alert's end was first seen: such an alert is ended, not standing. Before `covers_from` nothing is known: never present the list as the whole day. Fetch at most once a minute, while it is on screen.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The official messages of the last 24 hours.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=30, s-maxage=30"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/official.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "503": {
            "description": "No history yet: the server has not had its first valid state since it started.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_history"
                }
              }
            }
          }
        }
      }
    },
    "/v1/radiation": {
      "get": {
        "operationId": "getRadiation",
        "summary": "Gamma dose rate at PAA's stations in Poland",
        "description": "The latest reading of ambient gamma dose rate at each of the 62 stations of Państwowa Agencja Atomistyki (PAA), with its time (contract radiation.v1). Taken from PAA once an hour; PAA publishes an hour about two hours after it ends. Context only: SanAlert quotes the stations' own readings and scores nothing, and it never changes a level. A station with no reading in the last 24 hours is listed with no value. Show the attribution with the readings: © Państwowa Agencja Atomistyki, monitoring.paa.gov.pl, CC BY 3.0 PL.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "PAA's latest reading at every station.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=300, s-maxage=300"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/radiation.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "503": {
            "description": "PAA has not answered once since the server started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_data"
                }
              }
            }
          }
        }
      }
    },
    "/v1/nights": {
      "get": {
        "operationId": "getNights",
        "summary": "The index of night reports, newest first",
        "description": "One entry per night (18:00 to 06:00 in Poland) with a report, newest first, at most 60: whether anything happened (`notable`), the night's summary, the raised voivodeships and each one's highest level (`peaks`). A voivodeship not in `peaks` was quiet whenever levels were valid; `blind_min` above 0 means the night had a gap in the data, to be said rather than called quiet. A night with no entry has no report: say that, never quiet. Before the first report: `generated_at: null` and no nights.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The index.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=600, s-maxage=3600"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/nights-index.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          }
        }
      }
    },
    "/v1/nights/{night}": {
      "get": {
        "operationId": "getNight",
        "summary": "One night's report, voivodeship by voivodeship",
        "description": "How SanAlert's level moved in each voivodeship and what moved it, the official air warnings and when they were first seen, and whether SanAlert's level rose before them, after them or not at all. The levels are replayed from recorded source data through the same model, not recorded: they may differ by seconds from what a reader saw. A miss is said as plainly as a lead; a `blind` stretch is no data, never quiet; RCB's classes are tiers (\"tier 2 of 3\"), never SanAlert's level words.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          },
          {
            "name": "night",
            "in": "path",
            "required": true,
            "description": "The evening's date in Poland: the night of 21 to 22 September 2026 is `2026-09-21`.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The night's report.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=600, s-maxage=3600"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/night.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "404": {
            "description": "No report for that night.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=60"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_report"
                }
              }
            }
          }
        }
      }
    },
    "/v1/nights/{night}/replay": {
      "get": {
        "operationId": "getNightReplay",
        "summary": "One night on the map: the replay behind its report",
        "description": "The night of `/v1/nights/{night}` drawn instead of told (contract replay.v1): our level at every cell and voivodeship, the objects NEPTUN tracked, the UA alarms and the night's official warnings, as steps in whole seconds after `from`. Offered only for a night whose index entry says `replay: true`. The same rules as the report: a replay, not a recording; inside `blind` there is no data, never quiet, and nothing from before a blind stretch is carried across it; an `alarm_gaps` stretch is no data about alarms, never no alarms. NEPTUN's link stays visible next to its data and the `notice` with it. Sent gzip-compressed as stored to a client that accepts gzip (about 35 KB on an ordinary night), unpacked otherwise.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          },
          {
            "name": "night",
            "in": "path",
            "required": true,
            "description": "The evening's date in Poland, as for the report.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The night's replay.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=600, s-maxage=3600"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Vary": {
                "description": "The answer depends on Accept-Encoding: the stored gzip, or unpacked.",
                "schema": {
                  "type": "string"
                },
                "example": "Accept-Encoding"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/replay.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "404": {
            "description": "No replay for that night (not offered by the index, or not built).",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=60"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_replay"
                }
              }
            }
          },
          "503": {
            "description": "The replay cannot be read right now (the database is away); nothing caches this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "replay_unavailable"
                }
              }
            }
          }
        }
      }
    },
    "/v1/record": {
      "get": {
        "operationId": "getRecord",
        "summary": "The reliability record: SanAlert's levels against the official air warnings",
        "description": "For every official RCB or RSO air warning the recorder first saw live, voivodeship by voivodeship: was SanAlert's level already raised there (`ahead`), raised within an hour after (`late`), not raised (`missed`), or without data (`blind`); plus runs at `approaching` or higher with no official warning near them (`unprompted`), and the time without data. Rebuilt once a night. The numbers are small and the period short: say so next to them, with `period`, `model.status` and what each outcome means. The official warnings are not the only truth: a warning can come late or not at all.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The record.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=600, s-maxage=3600"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/record.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "404": {
            "description": "No record yet: the nightly job has not built one.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=60"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_record"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sources": {
      "get": {
        "operationId": "getSources",
        "summary": "One card per source: what it is, how sure it is, what it cannot see, its licence",
        "description": "Every source or layer SanAlert shows, in Polish, English and Ukrainian, in the order to show them. A `feed` card's freshness is `health.sources[key]` of `/v1/state`. NEPTUN's link stays a condition of using its data: these cards do not replace the attribution. Changes only with a release: fetch it at most once a day, with `If-None-Match`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The source cards.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=3600, s-maxage=86400"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/sources.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          }
        }
      }
    },
    "/v1/shelters": {
      "get": {
        "operationId": "getShelters",
        "summary": "The official register of shelter points in Poland, whole",
        "description": "The State Fire Service's register of shelter points (Komenda Główna Państwowej Straży Pożarnej, dane.gov.pl dataset 28058, CC BY 4.0), about 86 000 points, served whole so that the nearest are found on the device and no request carries a place. `points[i]` is `[lat, lon, availability, address]`: availability `0` on request (someone has to open it), `1` round the clock, `2` at set hours, `-1` not stated; the address verbatim. A point is a \"shelter point\" from the official register, not a hardened shelter. Attribute it wherever points are shown (the source's name, CC BY 4.0 and `data_date`); distances are straight lines; when the nearest point is far, the register has none nearby, which is not \"no shelter\". Official instructions come first: a list of points is never a reason to go outside during an alarm. Gzipped (about 1.4 MB) for a client that accepts gzip. Fetch it at most once a week, with `If-None-Match`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "The register.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=86400, s-maxage=86400"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              },
              "Vary": {
                "description": "The body is gzipped for a client that accepts it.",
                "schema": {
                  "type": "string"
                },
                "example": "Accept-Encoding"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/shelters.v1"
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          },
          "503": {
            "description": "No register yet: the server has not downloaded it since it started.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/error"
                },
                "example": {
                  "error": "no_register"
                }
              }
            }
          }
        }
      }
    },
    "/v1/grid/1.json": {
      "get": {
        "operationId": "getGrid",
        "summary": "Poland's grid: every cell and the voivodeship it belongs to",
        "description": "Every cell of the grid that belongs to Poland, with the voivodeship it rolls up to (`cells`), and the cells within 100 km of each Ukrainian oblast on Poland's border (`near_ua_oblast`). A cell's id is `\"i:j\"` with `i = floor(lat * 10 + 1e-9)` and `j = floor(lon * 20 / 3 + 1e-9)`; use exactly this formula, the epsilon included (`50.9 / 0.1` is `508.999…` in IEEE doubles). Its centre is `((i + 0.5) * 0.10, (j + 0.5) * 0.15)`. A cell that is not listed never has a level.",
        "responses": {
          "200": {
            "description": "The grid.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=86400, immutable"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/grid.v1"
                }
              }
            }
          }
        }
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "What the running server says about itself, for monitoring",
        "description": "Always `200` while the process answers; what the data is worth is in the body. Stable fields: `status`, `reasons`, `levels_valid`, `age_s` (the published state's age at this request, in seconds), `generated_at`, and per source `status`, `age_s` and `reason`. The other fields are the server's own and may change. A client reads the levels and their health from `/v1/state`, not from here.",
        "responses": {
          "200": {
            "description": "The server's health.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/health.v1"
                }
              }
            }
          }
        }
      },
      "head": {
        "operationId": "headHealth",
        "summary": "Whether the process answers",
        "description": "The same status and headers as `GET`, with no body.",
        "responses": {
          "200": {
            "description": "The process answers.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "no-store"
              }
            }
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "operationId": "getOpenapi",
        "summary": "This document",
        "description": "The OpenAPI description of SanAlert's public API, with the conditions of use. Changes only with a release.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IfNoneMatch"
          }
        ],
        "responses": {
          "200": {
            "description": "This document.",
            "headers": {
              "Cache-Control": {
                "description": "How long the answer may be kept.",
                "schema": {
                  "type": "string"
                },
                "example": "public, max-age=3600, s-maxage=86400"
              },
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "openapi",
                    "info",
                    "paths"
                  ]
                }
              }
            }
          },
          "304": {
            "$ref": "#/components/responses/NotModified"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "history.v1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://sanalert.pl/contract/history.v1.schema.json",
        "title": "SanAlert history v1 (contract/history.v1.md)",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "covers_from",
          "cells",
          "gaps",
          "official"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "generated_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "covers_from": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "cells": {
            "type": "object",
            "propertyNames": {
              "pattern": "^-?\\d+:-?\\d+$"
            },
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "object",
                "required": [
                  "level",
                  "from",
                  "to",
                  "codes",
                  "kinds",
                  "oblasts",
                  "km_min"
                ],
                "properties": {
                  "level": {
                    "enum": [
                      "activity",
                      "approaching",
                      "near"
                    ]
                  },
                  "from": {
                    "type": "string",
                    "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                  },
                  "to": {
                    "anyOf": [
                      {
                        "type": "string",
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "codes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "kinds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "oblasts": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "km_min": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0
                  }
                }
              }
            }
          },
          "gaps": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "from",
                "to"
              ],
              "properties": {
                "from": {
                  "type": "string",
                  "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                },
                "to": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          },
          "official": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "source",
                "class",
                "title",
                "regions",
                "url",
                "cancelled",
                "seen_from",
                "seen_to"
              ],
              "properties": {
                "source": {
                  "enum": [
                    "rcb",
                    "rso"
                  ]
                },
                "class": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "powiats": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[0-9]{4}$"
                  }
                },
                "url": {
                  "type": "string"
                },
                "cancelled": {
                  "type": "boolean"
                },
                "seen_from": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "seen_to": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "official.v1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://sanalert.pl/contract/official.v1.schema.json",
        "title": "SanAlert official messages of the last 24 hours v1 (contract/official.v1.md)",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "covers_from",
          "official"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "generated_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "covers_from": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "official": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "source",
                "class",
                "title",
                "regions",
                "url",
                "cancelled",
                "seen_from",
                "seen_to",
                "cancelled_seen"
              ],
              "properties": {
                "source": {
                  "enum": [
                    "rcb",
                    "rso"
                  ]
                },
                "class": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "powiats": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[0-9]{4}$"
                  }
                },
                "url": {
                  "type": "string"
                },
                "cancelled": {
                  "type": "boolean"
                },
                "seen_from": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "seen_to": {
                  "anyOf": [
                    {
                      "type": "string",
                      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "cancelled_seen": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                }
              }
            }
          }
        }
      },
      "sources.v1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://sanalert.pl/contract/sources.v1.schema.json",
        "title": "SanAlert source cards v1 (contract/sources.v1.md)",
        "type": "object",
        "required": [
          "v",
          "changed",
          "sources"
        ],
        "additionalProperties": false,
        "$defs": {
          "text": {
            "type": "object",
            "required": [
              "pl",
              "en",
              "uk"
            ],
            "additionalProperties": false,
            "properties": {
              "pl": {
                "type": "string",
                "minLength": 1
              },
              "en": {
                "type": "string",
                "minLength": 1
              },
              "uk": {
                "type": "string",
                "minLength": 1
              }
            }
          },
          "url": {
            "type": "string",
            "pattern": "^https://"
          }
        },
        "properties": {
          "v": {
            "const": 1
          },
          "changed": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "sources": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "id",
                "kind",
                "name",
                "url",
                "freshness",
                "title",
                "what",
                "sure",
                "limits",
                "licence",
                "licence_url",
                "surfaces"
              ],
              "additionalProperties": false,
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[a-z][a-z0-9_]*$"
                },
                "kind": {
                  "enum": [
                    "ours",
                    "leading",
                    "official",
                    "context",
                    "reference"
                  ]
                },
                "name": {
                  "type": "string",
                  "minLength": 1
                },
                "url": {
                  "type": "object",
                  "required": [
                    "pl",
                    "en",
                    "uk"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "pl": {
                      "$ref": "#/$defs/url"
                    },
                    "en": {
                      "$ref": "#/$defs/url"
                    },
                    "uk": {
                      "$ref": "#/$defs/url"
                    }
                  }
                },
                "freshness": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "kind",
                        "key"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "kind": {
                          "const": "feed"
                        },
                        "key": {
                          "type": "string",
                          "minLength": 1
                        }
                      }
                    },
                    {
                      "type": "object",
                      "required": [
                        "kind"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "kind": {
                          "enum": [
                            "document",
                            "register",
                            "dataset"
                          ]
                        }
                      }
                    }
                  ]
                },
                "title": {
                  "$ref": "#/$defs/text"
                },
                "what": {
                  "$ref": "#/$defs/text"
                },
                "sure": {
                  "$ref": "#/$defs/text"
                },
                "limits": {
                  "$ref": "#/$defs/text"
                },
                "licence": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/text"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "licence_url": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/url"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "surfaces": {
                  "type": "array",
                  "minItems": 1,
                  "uniqueItems": true,
                  "items": {
                    "enum": [
                      "web",
                      "ios",
                      "mac",
                      "android",
                      "wear",
                      "watch"
                    ]
                  }
                }
              }
            }
          }
        }
      },
      "state.v1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://api.sanalert.pl/contract/state.v1.schema.json",
        "title": "SanAlert state v1",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "sequence",
          "health",
          "levels_valid",
          "objects",
          "cells",
          "regions",
          "ua_alarms",
          "official",
          "notice",
          "attribution"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "env": {
            "enum": [
              "dev",
              "staging",
              "prod"
            ]
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "sequence": {
            "type": "integer",
            "minimum": 0
          },
          "health": {
            "type": "object",
            "required": [
              "status",
              "sources"
            ],
            "properties": {
              "status": {
                "$ref": "#/$defs/health_status"
              },
              "sources": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "required": [
                    "status",
                    "age_s"
                  ],
                  "properties": {
                    "status": {
                      "$ref": "#/$defs/health_status"
                    },
                    "age_s": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "levels_valid": {
            "type": "boolean"
          },
          "objects": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "kind",
                "lat",
                "lon",
                "course_deg",
                "speed_kmh",
                "uncertainty_km",
                "confidence",
                "reports"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "kind": {
                  "enum": [
                    "drone",
                    "recon",
                    "cruise_missile",
                    "ballistic",
                    "unknown"
                  ]
                },
                "lat": {
                  "type": "number"
                },
                "lon": {
                  "type": "number"
                },
                "course_deg": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0,
                  "maximum": 360
                },
                "course_presumed": {
                  "type": "boolean"
                },
                "speed_kmh": {
                  "type": "integer"
                },
                "uncertainty_km": {
                  "type": "number"
                },
                "confidence": {
                  "enum": [
                    "low",
                    "medium",
                    "high"
                  ]
                },
                "reports": {
                  "type": "integer"
                },
                "updated_at": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "trail": {
                  "type": "array",
                  "items": {
                    "type": "array",
                    "prefixItems": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "number"
                      },
                      {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    ]
                  }
                },
                "count": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 1
                },
                "count_at_least": {
                  "type": "boolean"
                },
                "area_only": {
                  "type": "boolean"
                },
                "position": {
                  "enum": [
                    "confirmed",
                    "approx",
                    null
                  ]
                },
                "locality": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "oblast": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "toward_locality": {
                  "type": "boolean"
                },
                "speed_measured_kmh": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "raises": {
                  "enum": [
                    "activity",
                    "approaching",
                    "near",
                    null
                  ]
                },
                "side": {
                  "const": "russia"
                },
                "country": {
                  "const": "RU"
                }
              }
            }
          },
          "aircraft": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "side",
                "lat",
                "lon"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "country": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "pattern": "^[A-Z]{2}$"
                },
                "side": {
                  "enum": [
                    "poland",
                    "nato",
                    "russia",
                    "other"
                  ]
                },
                "callsign": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "type": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "rotorcraft": {
                  "type": "boolean"
                },
                "lat": {
                  "type": "number"
                },
                "lon": {
                  "type": "number"
                },
                "alt_m": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "speed_kmh": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "course_deg": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "minimum": 0,
                  "maximum": 359
                },
                "seen_s": {
                  "type": "integer"
                }
              }
            }
          },
          "cells": {
            "type": "object",
            "propertyNames": {
              "pattern": "^-?\\d+:-?\\d+$"
            },
            "additionalProperties": {
              "type": "object",
              "required": [
                "level",
                "risk",
                "confidence",
                "drivers"
              ],
              "properties": {
                "level": {
                  "$ref": "#/$defs/level"
                },
                "risk": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100
                },
                "confidence": {
                  "enum": [
                    "low",
                    "medium",
                    "high"
                  ]
                },
                "drivers": {
                  "type": "array",
                  "maxItems": 3,
                  "items": {
                    "type": "object",
                    "required": [
                      "code"
                    ],
                    "properties": {
                      "code": {
                        "enum": [
                          "object_near",
                          "object_toward",
                          "object_nearby",
                          "ua_alarm_adjacent",
                          "area_warning",
                          "track_lost"
                        ]
                      },
                      "course_known": {
                        "type": "boolean"
                      },
                      "lost_at": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          },
          "regions": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "required": [
                "level",
                "cells"
              ],
              "properties": {
                "level": {
                  "$ref": "#/$defs/level"
                },
                "cells": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                }
              }
            }
          },
          "ua_alarms": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "oblast",
                "level"
              ],
              "properties": {
                "oblast": {
                  "type": "string"
                },
                "oblast_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "raion_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "raion": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "level": {
                  "enum": [
                    "red",
                    "yellow"
                  ]
                },
                "threat": {
                  "enum": [
                    "drone",
                    "missile",
                    "ballistic",
                    "aviation",
                    "glide_bomb",
                    "other"
                  ]
                },
                "since": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "official": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "source",
                "class",
                "title",
                "regions",
                "url"
              ],
              "properties": {
                "source": {
                  "enum": [
                    "rcb",
                    "rso"
                  ]
                },
                "class": {
                  "enum": [
                    "shelter",
                    "heightened",
                    "precaution",
                    "cancellation",
                    "exercise",
                    "other_air"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "text": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "regions_in_force": {
                  "type": [
                    "array",
                    "null"
                  ],
                  "items": {
                    "type": "string"
                  }
                },
                "powiats": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "pattern": "^[0-9]{4}$"
                  }
                },
                "url": {
                  "type": "string"
                },
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "cancelled": {
                  "type": "boolean"
                },
                "first_seen": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "cancelled_seen": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "updates": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "class",
                      "text",
                      "regions",
                      "ended",
                      "again",
                      "seen"
                    ],
                    "properties": {
                      "again": {
                        "type": "boolean"
                      },
                      "class": {
                        "enum": [
                          "shelter",
                          "heightened",
                          "precaution",
                          "cancellation",
                          "exercise",
                          "other_air",
                          "not_air"
                        ]
                      },
                      "text": {
                        "type": "string"
                      },
                      "regions": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "ended": {
                        "type": "boolean"
                      },
                      "seen": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      },
                      "via": {
                        "enum": [
                          "rso"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "radiation": {
            "description": "PAA's current gamma dose rate readings above PAA's own line (`above`, 0.3 µSv/h: \"wymaga weryfikacji źródła\"), each as /v1/radiation gives it (contract/radiation.v1.md), sorted by id; a reading more than 6 hours old is not listed. Clients draw these even with the radiation layer off. Display only: never a level. null: PAA's readings are unavailable (none yet, or the last more than 3 hours old), never \"nothing raised\"; [] in `stations`: none above PAA's line.",
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "required": [
                  "above",
                  "unit",
                  "stations"
                ],
                "properties": {
                  "above": {
                    "type": "number"
                  },
                  "unit": {
                    "type": "string"
                  },
                  "stations": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "id",
                        "name",
                        "lat",
                        "lon",
                        "value",
                        "unit",
                        "at"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
                        },
                        "name": {
                          "type": "string",
                          "minLength": 1
                        },
                        "lat": {
                          "type": "number",
                          "minimum": 48.9,
                          "maximum": 55.1
                        },
                        "lon": {
                          "type": "number",
                          "minimum": 13.9,
                          "maximum": 24.3
                        },
                        "value": {
                          "type": "number",
                          "exclusiveMinimum": 0
                        },
                        "unit": {
                          "type": "string"
                        },
                        "at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            ]
          },
          "notice": {
            "type": "object",
            "required": [
              "pl",
              "en"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "attribution": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "url"
              ]
            }
          }
        },
        "$defs": {
          "level": {
            "enum": [
              "quiet",
              "activity",
              "approaching",
              "near"
            ]
          },
          "health_status": {
            "enum": [
              "ok",
              "degraded",
              "unknown"
            ]
          }
        }
      },
      "error": {
        "description": "A refusal: the code says why.",
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "compact.v1": {
        "description": "The compact state for watches (`/v1/compact`).",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "status",
          "levels_valid",
          "cells",
          "regions",
          "official",
          "notice",
          "attribution"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "The state document's own."
          },
          "status": {
            "enum": [
              "ok",
              "degraded",
              "unknown"
            ],
            "description": "The state's `health.status`."
          },
          "levels_valid": {
            "type": "boolean"
          },
          "cells": {
            "type": "object",
            "description": "Every cell above quiet, by grid row `i`: runs of `[first j, last j, level]`, level `1` activity, `2` approaching, `3` near.",
            "propertyNames": {
              "pattern": "^-?\\d+$"
            },
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "array",
                "minItems": 3,
                "maxItems": 3,
                "prefixItems": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "integer"
                  },
                  {
                    "enum": [
                      1,
                      2,
                      3
                    ]
                  }
                ]
              }
            }
          },
          "regions": {
            "type": "object",
            "description": "Voivodeships above quiet, and their level (1 to 3).",
            "additionalProperties": {
              "enum": [
                1,
                2,
                3
              ]
            }
          },
          "official": {
            "type": "object",
            "required": [
              "available",
              "now",
              "ended"
            ],
            "properties": {
              "available": {
                "type": "boolean",
                "description": "`false`: RCB or RSO could not be read; say so, never \"no alerts\"."
              },
              "now": {
                "type": "array",
                "description": "What is in force, one line per class, strongest first.",
                "items": {
                  "type": "object",
                  "required": [
                    "class",
                    "sources",
                    "regions",
                    "since"
                  ],
                  "properties": {
                    "class": {
                      "type": "string",
                      "description": "`shelter`, `heightened`, `precaution` or `other_air`."
                    },
                    "sources": {
                      "type": "array",
                      "items": {
                        "enum": [
                          "rcb",
                          "rso"
                        ]
                      }
                    },
                    "regions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "since": {
                      "oneOf": [
                        {
                          "type": "object",
                          "required": [
                            "kind",
                            "at",
                            "local"
                          ],
                          "properties": {
                            "kind": {
                              "enum": [
                                "published",
                                "update",
                                "valid_from",
                                "ended"
                              ]
                            },
                            "at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "local": {
                              "type": "string",
                              "description": "The time in Poland, `21:43`, or `24.09 23:10` on another day."
                            }
                          }
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              },
              "ended": {
                "description": "With nothing in force: today's RCB alert that ended last, or `null`.",
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "type": "object",
                    "required": [
                      "regions",
                      "at",
                      "alert"
                    ],
                    "properties": {
                      "regions": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "at": {
                        "oneOf": [
                          {
                            "type": "object",
                            "required": [
                              "kind",
                              "at",
                              "local"
                            ],
                            "properties": {
                              "kind": {
                                "enum": [
                                  "published",
                                  "update",
                                  "valid_from",
                                  "ended"
                                ]
                              },
                              "at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "local": {
                                "type": "string",
                                "description": "The time in Poland, `21:43`, or `24.09 23:10` on another day."
                              }
                            }
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "alert": {
                        "oneOf": [
                          {
                            "type": "object",
                            "required": [
                              "kind",
                              "at",
                              "local"
                            ],
                            "properties": {
                              "kind": {
                                "enum": [
                                  "published",
                                  "update",
                                  "valid_from",
                                  "ended"
                                ]
                              },
                              "at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "local": {
                                "type": "string",
                                "description": "The time in Poland, `21:43`, or `24.09 23:10` on another day."
                              }
                            }
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          "notice": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "The state's, word for word."
          },
          "attribution": {
            "type": "array",
            "description": "The state's, word for word.",
            "items": {
              "type": "object",
              "required": [
                "name",
                "url"
              ]
            }
          }
        }
      },
      "health.v1": {
        "description": "`/v1/health`. Stable: `status`, `reasons`, `levels_valid`, `age_s`, `generated_at`, and per source `status`, `age_s`, `reason`.",
        "type": "object",
        "required": [
          "status",
          "reasons",
          "sources",
          "levels_valid",
          "age_s",
          "generated_at"
        ],
        "properties": {
          "status": {
            "enum": [
              "ok",
              "degraded",
              "unknown"
            ]
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": [
              "object",
              "null"
            ],
            "description": "As `health.sources` in the state; `null` before the first state.",
            "additionalProperties": {
              "type": "object",
              "required": [
                "status",
                "age_s"
              ],
              "properties": {
                "status": {
                  "enum": [
                    "ok",
                    "degraded",
                    "unknown"
                  ]
                },
                "age_s": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "levels_valid": {
            "type": "boolean"
          },
          "age_s": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The published state's age at this request; `null` before the first."
          },
          "generated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sequence": {
            "type": "integer"
          },
          "model_errors": {
            "type": "integer"
          },
          "version": {
            "type": "string"
          },
          "env": {
            "type": "string"
          }
        }
      },
      "nights-index.v1": {
        "description": "The index of night reports (`/v1/nights`).",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "nights"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "generated_at": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "nights": {
            "type": "array",
            "maxItems": 60,
            "items": {
              "type": "object",
              "required": [
                "night",
                "notable",
                "peak",
                "raised",
                "warnings",
                "outcomes",
                "blind_min",
                "voivodeships",
                "peaks"
              ],
              "properties": {
                "night": {
                  "type": "string",
                  "format": "date"
                },
                "notable": {
                  "type": "boolean"
                },
                "peak": {
                  "oneOf": [
                    {
                      "enum": [
                        "activity",
                        "approaching",
                        "near"
                      ]
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "raised": {
                  "type": "integer",
                  "minimum": 0
                },
                "warnings": {
                  "type": "integer",
                  "minimum": 0
                },
                "outcomes": {
                  "type": "object",
                  "required": [
                    "ahead",
                    "late",
                    "missed",
                    "blind"
                  ],
                  "properties": {
                    "ahead": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "late": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "missed": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "blind": {
                      "type": "integer",
                      "minimum": 0
                    }
                  }
                },
                "blind_min": {
                  "type": "integer",
                  "minimum": 0
                },
                "voivodeships": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "peaks": {
                  "type": "object",
                  "additionalProperties": {
                    "enum": [
                      "activity",
                      "approaching",
                      "near"
                    ]
                  }
                },
                "replay": {
                  "type": "boolean",
                  "description": "The night has a replay on the map (`/v1/nights/{night}/replay`), built with the same model as the report."
                }
              }
            }
          }
        }
      },
      "night.v1": {
        "description": "One night's report (`/v1/nights/{night}`), 18:00 to 06:00 in Poland.",
        "type": "object",
        "required": [
          "v",
          "night",
          "from",
          "to",
          "generated_at",
          "model",
          "rules",
          "notable",
          "summary",
          "voivodeships",
          "warnings",
          "blind"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "night": {
            "type": "string",
            "format": "date"
          },
          "from": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "to": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "generated_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "model": {
            "type": "object",
            "required": [
              "version",
              "status",
              "fingerprint"
            ],
            "properties": {
              "version": {
                "type": "integer"
              },
              "status": {
                "type": "string"
              },
              "fingerprint": {
                "type": "string"
              }
            }
          },
          "rules": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "notable": {
            "type": "boolean"
          },
          "summary": {
            "type": "object",
            "required": [
              "peak",
              "raised",
              "warnings",
              "outcomes",
              "blind_min"
            ],
            "properties": {
              "peak": {
                "oneOf": [
                  {
                    "enum": [
                      "activity",
                      "approaching",
                      "near"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "raised": {
                "type": "integer"
              },
              "warnings": {
                "type": "integer"
              },
              "outcomes": {
                "type": "object",
                "required": [
                  "ahead",
                  "late",
                  "missed",
                  "blind"
                ],
                "properties": {
                  "ahead": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "late": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "missed": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "blind": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              },
              "blind_min": {
                "type": "integer"
              }
            }
          },
          "voivodeships": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "voivodeship",
                "peak",
                "minutes",
                "episodes"
              ],
              "properties": {
                "voivodeship": {
                  "type": "string"
                },
                "peak": {
                  "oneOf": [
                    {
                      "enum": [
                        "activity",
                        "approaching",
                        "near"
                      ]
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "`null`: named by a warning, never raised."
                },
                "minutes": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "integer"
                  }
                },
                "episodes": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "from",
                      "to",
                      "peak",
                      "steps"
                    ]
                  }
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "first_seen_at",
                "class",
                "regions",
                "areas"
              ]
            }
          },
          "blind": {
            "type": "array",
            "description": "Stretches without data: `[from, to]`.",
            "items": {
              "type": "array",
              "minItems": 2,
              "maxItems": 2,
              "items": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
              }
            }
          }
        }
      },
      "record.v1": {
        "description": "The reliability record (`/v1/record`).",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "period",
          "model",
          "rules",
          "warnings",
          "summary",
          "unprompted",
          "time_at_level",
          "blind",
          "archive_only"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "generated_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "period": {
            "type": "object",
            "required": [
              "from",
              "to",
              "days"
            ],
            "properties": {
              "from": {
                "type": [
                  "string",
                  "null"
                ],
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
              },
              "to": {
                "type": [
                  "string",
                  "null"
                ],
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
              },
              "days": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "model": {
            "type": "object",
            "required": [
              "version",
              "status",
              "fingerprint"
            ],
            "properties": {
              "version": {
                "type": "integer"
              },
              "status": {
                "type": "string"
              },
              "fingerprint": {
                "type": "string"
              }
            }
          },
          "rules": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "first_seen_at",
                "ended_at",
                "class",
                "sources",
                "text",
                "url",
                "regions",
                "areas"
              ],
              "properties": {
                "first_seen_at": {
                  "type": "string",
                  "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                },
                "ended_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                },
                "class": {
                  "enum": [
                    "shelter",
                    "heightened",
                    "precaution",
                    "other_air"
                  ]
                },
                "sources": {
                  "type": "array",
                  "items": {
                    "enum": [
                      "rcb",
                      "rso"
                    ]
                  }
                },
                "text": {
                  "type": "string"
                },
                "url": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "areas": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "voivodeship",
                      "outcome",
                      "activity",
                      "approaching"
                    ],
                    "properties": {
                      "voivodeship": {
                        "type": "string"
                      },
                      "outcome": {
                        "enum": [
                          "ahead",
                          "late",
                          "missed",
                          "blind"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "summary": {
            "type": "object",
            "required": [
              "warnings",
              "areas",
              "outcomes",
              "median_lead_min"
            ],
            "properties": {
              "warnings": {
                "type": "integer"
              },
              "areas": {
                "type": "integer"
              },
              "outcomes": {
                "type": "object",
                "required": [
                  "ahead",
                  "late",
                  "missed",
                  "blind"
                ],
                "properties": {
                  "ahead": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "late": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "missed": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "blind": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              },
              "median_lead_min": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "unprompted": {
            "type": "object",
            "required": [
              "count",
              "minutes",
              "brief",
              "runs"
            ]
          },
          "time_at_level": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "additionalProperties": {
                "type": "integer"
              }
            }
          },
          "blind": {
            "type": "object",
            "required": [
              "minutes",
              "longest_min",
              "stretches"
            ]
          },
          "archive_only": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "shelters.v1": {
        "description": "The register of shelter points (`/v1/shelters`).",
        "type": "object",
        "required": [
          "v",
          "source",
          "data_date",
          "fetched_at",
          "points"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "source": {
            "type": "object",
            "required": [
              "name",
              "url",
              "licence"
            ],
            "properties": {
              "name": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "licence": {
                "type": "string"
              }
            }
          },
          "data_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "The date of the register's data; `null` if it did not say."
          },
          "fetched_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "array",
              "minItems": 4,
              "maxItems": 4,
              "prefixItems": [
                {
                  "type": "number",
                  "description": "Latitude, WGS 84"
                },
                {
                  "type": "number",
                  "description": "Longitude, WGS 84"
                },
                {
                  "enum": [
                    -1,
                    0,
                    1,
                    2
                  ],
                  "description": "0 on request, 1 round the clock, 2 at set hours, -1 not stated"
                },
                {
                  "type": "string",
                  "description": "The address, verbatim"
                }
              ]
            }
          }
        }
      },
      "grid.v1": {
        "description": "Poland's grid, version 1 (`/v1/grid/1.json`).",
        "type": "object",
        "required": [
          "v",
          "lat_step",
          "lon_step",
          "formula",
          "cells",
          "near_ua_oblast"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "lat_step": {
            "const": 0.1
          },
          "lon_step": {
            "const": 0.15
          },
          "formula": {
            "type": "string"
          },
          "cells": {
            "type": "object",
            "description": "Cell id → the voivodeship it rolls up to.",
            "propertyNames": {
              "pattern": "^-?\\d+:-?\\d+$"
            },
            "additionalProperties": {
              "type": "string"
            }
          },
          "near_ua_oblast": {
            "type": "object",
            "description": "Ukrainian oblast id → the cells near it.",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^-?\\d+:-?\\d+$"
              }
            }
          }
        }
      },
      "replay.v1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://sanalert.pl/contract/replay.v1.schema.json",
        "title": "SanAlert night replay v1 (contract/replay.v1.md)",
        "type": "object",
        "required": [
          "v",
          "night",
          "from",
          "to",
          "generated_at",
          "model",
          "t",
          "blind",
          "alarm_gaps",
          "regions",
          "cells",
          "alarms",
          "fields",
          "objects",
          "warnings",
          "crowding",
          "notice",
          "attribution"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "night": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "from": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "to": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "generated_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "model": {
            "type": "object",
            "required": [
              "version",
              "status",
              "fingerprint"
            ],
            "properties": {
              "version": {
                "type": "integer"
              },
              "status": {
                "type": "string"
              },
              "fingerprint": {
                "type": "string"
              }
            }
          },
          "t": {
            "const": "s"
          },
          "blind": {
            "type": "array",
            "items": {
              "type": "array",
              "prefixItems": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "integer",
                  "minimum": 0
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "alarm_gaps": {
            "type": "array",
            "items": {
              "type": "array",
              "prefixItems": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "integer",
                  "minimum": 0
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "regions": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "minItems": 1,
              "items": {
                "type": "array",
                "prefixItems": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "enum": [
                      "quiet",
                      "activity",
                      "approaching",
                      "near"
                    ]
                  }
                ],
                "minItems": 2,
                "maxItems": 2
              }
            }
          },
          "cells": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "minItems": 1,
              "items": {
                "type": "array",
                "prefixItems": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "enum": [
                      "quiet",
                      "activity",
                      "approaching",
                      "near"
                    ]
                  }
                ],
                "minItems": 2,
                "maxItems": 2
              }
            },
            "propertyNames": {
              "pattern": "^[0-9]+:[0-9]+$"
            }
          },
          "alarms": {
            "type": "object",
            "propertyNames": {
              "pattern": "^(oblast:)?[a-z-]+$"
            },
            "additionalProperties": {
              "type": "array",
              "minItems": 1,
              "items": {
                "oneOf": [
                  {
                    "type": "array",
                    "prefixItems": [
                      {
                        "type": "integer",
                        "minimum": 0
                      },
                      {
                        "enum": [
                          "red",
                          "yellow"
                        ]
                      },
                      {
                        "enum": [
                          "drone",
                          "missile",
                          "ballistic",
                          "aviation",
                          "glide_bomb",
                          "other"
                        ]
                      }
                    ],
                    "minItems": 3,
                    "maxItems": 3
                  },
                  {
                    "type": "array",
                    "prefixItems": [
                      {
                        "type": "integer",
                        "minimum": 0
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "minItems": 2,
                    "maxItems": 2
                  }
                ]
              }
            }
          },
          "fields": {
            "const": [
              "t",
              "kind",
              "lat",
              "lon",
              "course_deg",
              "course_presumed",
              "speed_kmh",
              "speed_measured_kmh",
              "uncertainty_km",
              "confidence",
              "position",
              "count",
              "count_at_least",
              "area_only",
              "locality",
              "oblast",
              "toward_locality",
              "raises"
            ]
          },
          "objects": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "points"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "points": {
                  "type": "array",
                  "minItems": 1,
                  "items": {
                    "oneOf": [
                      {
                        "type": "array",
                        "prefixItems": [
                          {
                            "type": "integer",
                            "minimum": 0
                          },
                          {
                            "enum": [
                              "drone",
                              "recon",
                              "cruise_missile",
                              "ballistic",
                              "unknown"
                            ]
                          },
                          {
                            "type": "number",
                            "minimum": -90,
                            "maximum": 90
                          },
                          {
                            "type": "number",
                            "minimum": -180,
                            "maximum": 180
                          },
                          {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "minimum": 0,
                            "maximum": 360
                          },
                          {
                            "type": "boolean"
                          },
                          {
                            "type": "integer"
                          },
                          {
                            "type": [
                              "integer",
                              "null"
                            ]
                          },
                          {
                            "type": "number",
                            "minimum": 0
                          },
                          {
                            "enum": [
                              "low",
                              "medium",
                              "high"
                            ]
                          },
                          {
                            "enum": [
                              "confirmed",
                              "approx",
                              null
                            ]
                          },
                          {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "minimum": 1
                          },
                          {
                            "type": "boolean"
                          },
                          {
                            "type": "boolean"
                          },
                          {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          {
                            "type": "boolean"
                          },
                          {
                            "enum": [
                              "activity",
                              "approaching",
                              "near",
                              null
                            ]
                          }
                        ],
                        "minItems": 18,
                        "maxItems": 18
                      },
                      {
                        "type": "array",
                        "prefixItems": [
                          {
                            "type": "integer",
                            "minimum": 0
                          }
                        ],
                        "minItems": 1,
                        "maxItems": 1
                      }
                    ]
                  }
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "t",
                "until",
                "end_seen",
                "class",
                "regions"
              ],
              "properties": {
                "t": {
                  "type": "integer",
                  "minimum": 0
                },
                "until": {
                  "type": "integer",
                  "minimum": 0
                },
                "end_seen": {
                  "type": "boolean"
                },
                "class": {
                  "type": "string"
                },
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "crowding": {
            "type": "object",
            "required": [
              "max_markers",
              "at",
              "max_overlapping_30km",
              "largest_group_30km"
            ],
            "properties": {
              "max_markers": {
                "type": "integer",
                "minimum": 0
              },
              "at": {
                "anyOf": [
                  {
                    "type": "integer",
                    "minimum": 0
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "max_overlapping_30km": {
                "type": "integer",
                "minimum": 0
              },
              "largest_group_30km": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "notice": {
            "type": "object",
            "required": [
              "pl",
              "en"
            ],
            "additionalProperties": {
              "type": "string"
            }
          },
          "attribution": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "name",
                "url"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "role": {
                  "type": "string"
                }
              }
            },
            "contains": {
              "type": "object",
              "properties": {
                "name": {
                  "const": "NEPTUN"
                }
              },
              "required": [
                "name"
              ]
            }
          }
        }
      },
      "radiation.v1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://sanalert.pl/contract/radiation.v1.schema.json",
        "title": "SanAlert gamma dose rate v1 (contract/radiation.v1.md)",
        "type": "object",
        "required": [
          "v",
          "generated_at",
          "source",
          "stations"
        ],
        "properties": {
          "v": {
            "const": 1
          },
          "generated_at": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
          },
          "source": {
            "type": "object",
            "required": [
              "name",
              "url",
              "licence",
              "licence_url",
              "attribution"
            ],
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1
              },
              "url": {
                "type": "string",
                "pattern": "^https://"
              },
              "licence": {
                "type": "string",
                "minLength": 1
              },
              "licence_url": {
                "type": "string",
                "pattern": "^https://"
              },
              "attribution": {
                "type": "string",
                "minLength": 1
              }
            }
          },
          "stations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "lat",
                "lon",
                "value",
                "unit",
                "at"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
                },
                "name": {
                  "type": "string",
                  "minLength": 1
                },
                "lat": {
                  "type": "number",
                  "minimum": 48.9,
                  "maximum": 55.1
                },
                "lon": {
                  "type": "number",
                  "minimum": 13.9,
                  "maximum": 24.3
                },
                "value": {
                  "oneOf": [
                    {
                      "type": "number",
                      "exclusiveMinimum": 0
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "unit": {
                  "const": "µSv/h"
                },
                "at": {
                  "oneOf": [
                    {
                      "type": "string",
                      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "parameters": {
      "IfNoneMatch": {
        "name": "If-None-Match",
        "in": "header",
        "required": false,
        "description": "The `ETag` of the copy you hold: the answer is `304` with no body while it is still current.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "ETag": {
        "description": "The version of the body: send it back in `If-None-Match`.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "NotModified": {
        "description": "Not modified: the copy whose `ETag` you sent is still current. No body.",
        "headers": {
          "ETag": {
            "$ref": "#/components/headers/ETag"
          }
        }
      }
    }
  }
}
