{
  "openapi": "3.1.0",
  "info": {
    "title": "Updraft API",
    "version": "0.1.0"
  },
  "paths": {
    "/v1/health": {
      "get": {
        "tags": [
          "meta"
        ],
        "summary": "Health",
        "operationId": "health_v1_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/v1/flights": {
      "post": {
        "tags": [
          "flights"
        ],
        "summary": "Upload a flight track (IGC, GPX or FIT)",
        "operationId": "upload_flight_v1_flights_post",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_upload_flight_v1_flights_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Same file already stored (`duplicate`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightReceipt"
                }
              }
            }
          },
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FlightReceipt"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "code: `flight_deleted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "413": {
            "description": "code: `file_too_large`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`, `igc_empty`, `igc_no_fix_records`, `igc_missing_date`, `igc_corrupt`, `igc_no_valid_fix`, `gpx_invalid`, `gpx_no_fix_records`, `gpx_missing_time`, `gpx_corrupt`, `gpx_no_valid_fix`, `fit_invalid`, `fit_checksum`, `fit_not_activity`, `fit_no_fix_records`, `fit_corrupt`, `fit_no_valid_fix`, `flight_date_future`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `queue_unavailable`, `storage_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "APIKeyHeader": []
          }
        ]
      }
    },
    "/v1/sites": {
      "get": {
        "tags": [
          "sites"
        ],
        "summary": "Approved takeoff and landing sites in a bounding box",
        "operationId": "list_sites_v1_sites_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "bbox",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 200,
              "description": "west,south,east,north in degrees (WGS 84); each side at most 20°, west < east (no antimeridian wrap)",
              "examples": [
                "28.5,36.0,30.0,37.2"
              ],
              "title": "Bbox"
            },
            "description": "west,south,east,north in degrees (WGS 84); each side at most 20°, west < east (no antimeridian wrap)"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "enum": [
                    "takeoff",
                    "landing"
                  ],
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Only this kind; both when omitted",
              "title": "Kind"
            },
            "description": "Only this kind; both when omitted"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 500,
              "minimum": 1,
              "description": "Sites per page",
              "default": 100,
              "title": "Limit"
            },
            "description": "Sites per page"
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "maxLength": 64
                },
                {
                  "type": "null"
                }
              ],
              "description": "`next_cursor` of the previous page",
              "title": "Cursor"
            },
            "description": "`next_cursor` of the previous page"
          }
        ],
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection, ordered by id; `ETag` and `Cache-Control` headers",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/SiteCollection"
                }
              }
            }
          },
          "304": {
            "description": "`If-None-Match` matched the current `ETag`; no body"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`, `bbox_invalid`, `bbox_too_large`, `cursor_invalid`",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sites/{site_id}/suitability": {
      "get": {
        "tags": [
          "sites"
        ],
        "summary": "Take-off suitability for a day from the wind forecast (pre-flight planning only)",
        "operationId": "site_suitability_v1_sites__site_id__suitability_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "site_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Take-off id (`GET /v1/sites`)",
              "title": "Site Id"
            },
            "description": "Take-off id (`GET /v1/sites`)"
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Local date (Europe/Istanbul), today to today + 4 days; default today",
              "examples": [
                "2026-11-06"
              ],
              "title": "Date"
            },
            "description": "Local date (Europe/Istanbul), today to today + 4 days; default today"
          }
        ],
        "responses": {
          "200": {
            "description": "Slots of the local day; `Cache-Control` header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuitabilityResponse"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `site_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`, `date_out_of_range`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/slope-conditions": {
      "get": {
        "tags": [
          "layers"
        ],
        "summary": "Sun position and forecast wind per hour for the slope layers (pre-flight only)",
        "operationId": "slope_conditions_v1_slope_conditions_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "maximum": 90,
              "minimum": -90,
              "description": "Latitude of the map view; snapped to its 0.25° cell",
              "title": "Lat"
            },
            "description": "Latitude of the map view; snapped to its 0.25° cell"
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number",
              "maximum": 180,
              "minimum": -180,
              "description": "Longitude of the map view; snapped to its 0.25° cell",
              "title": "Lon"
            },
            "description": "Longitude of the map view; snapped to its 0.25° cell"
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Local date (Europe/Istanbul), default today; the forecast covers today to today + 4 days, other dates give the sun only",
              "examples": [
                "2026-11-06"
              ],
              "title": "Date"
            },
            "description": "Local date (Europe/Istanbul), default today; the forecast covers today to today + 4 days, other dates give the sun only"
          }
        ],
        "responses": {
          "200": {
            "description": "Hours 05-21 of the local day; `Cache-Control` header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SlopeConditions"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`, `date_out_of_range`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/routes": {
      "get": {
        "tags": [
          "routes"
        ],
        "summary": "Route suggestions from a take-off for a day and hour (pre-flight planning only)",
        "description": "A few options in compass order, not ranked; each waypoint with landing alternatives, each option with reasons. Only where a region moderator marked the suggestions reasonable. The pilot decides; airspace and legal limits are checked separately (docs/10 §20).",
        "operationId": "routes_v1_routes_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "takeoff_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Public take-off id (`GET /v1/sites`)",
              "title": "Takeoff Id"
            },
            "description": "Public take-off id (`GET /v1/sites`)"
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Local date (Europe/Istanbul), today to today + 4 days; default today",
              "examples": [
                "2026-11-06"
              ],
              "title": "Date"
            },
            "description": "Local date (Europe/Istanbul), today to today + 4 days; default today"
          },
          {
            "name": "hour",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Local hour 8-19 (Europe/Istanbul); default the next whole hour",
              "examples": [
                12
              ],
              "title": "Hour"
            },
            "description": "Local hour 8-19 (Europe/Istanbul); default the next whole hour"
          }
        ],
        "responses": {
          "200": {
            "description": "Options; `Cache-Control` header",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutesResponse"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `site_not_found`, `routes_not_available`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`, `date_out_of_range`, `hour_out_of_range`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/packs/{region}": {
      "get": {
        "tags": [
          "packs"
        ],
        "summary": "Current offline pack of a region, with download URLs",
        "operationId": "get_pack_v1_packs__region__get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "region",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
              "description": "Region slug (`regions.slug`), e.g. `fethiye-babadag`",
              "title": "Region"
            },
            "description": "Region slug (`regions.slug`), e.g. `fethiye-babadag`"
          }
        ],
        "responses": {
          "200": {
            "description": "Pack description; `Cache-Control: private, no-store`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackInfo"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `pack_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `pack_download_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/packs/{region}/{pack_version}/{file}": {
      "get": {
        "tags": [
          "packs"
        ],
        "summary": "Download one pack file through a signed URL (no API key)",
        "operationId": "get_pack_file_v1_packs__region___pack_version___file__get",
        "parameters": [
          {
            "name": "region",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
              "description": "Region slug (`regions.slug`), e.g. `fethiye-babadag`",
              "title": "Region"
            },
            "description": "Region slug (`regions.slug`), e.g. `fethiye-babadag`"
          },
          {
            "name": "pack_version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{8}(?:-(?:[2-9]|[1-9]\\d+))?$",
              "description": "`YYYYMMDD[-n]`",
              "title": "Pack Version"
            },
            "description": "`YYYYMMDD[-n]`"
          },
          {
            "name": "file",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^(?:base|contours|terrain|hillshade|power)\\.pmtiles$",
              "description": "`{kind}.pmtiles`",
              "title": "File"
            },
            "description": "`{kind}.pmtiles`"
          },
          {
            "name": "expires",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "description": "Unix seconds, from the signed URL",
              "title": "Expires"
            },
            "description": "Unix seconds, from the signed URL"
          },
          {
            "name": "signature",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "description": "From the signed URL",
              "title": "Signature"
            },
            "description": "From the signed URL"
          }
        ],
        "responses": {
          "200": {
            "description": "The PMTiles file; `Range` requests answer 206",
            "content": {
              "application/vnd.pmtiles": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "307": {
            "description": "Object store presigned URL (S3 stores)"
          },
          "403": {
            "description": "code: `pack_url_invalid`, `pack_url_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `pack_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `pack_download_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/open-data": {
      "get": {
        "tags": [
          "open-data"
        ],
        "summary": "Published yearly open data packages (CC BY 4.0)",
        "operationId": "list_open_data_v1_open_data_get",
        "responses": {
          "200": {
            "description": "Releases; `Cache-Control: private, max-age=300`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenDataList"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "APIKeyHeader": []
          }
        ]
      }
    },
    "/v1/open-data/{year}/{file}": {
      "get": {
        "tags": [
          "open-data"
        ],
        "summary": "Download one file of a yearly open data package",
        "operationId": "get_open_data_file_v1_open_data__year___file__get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "year",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "maximum": 9999,
              "minimum": 2026,
              "description": "Release year",
              "title": "Year"
            },
            "description": "Release year"
          },
          {
            "name": "file",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^(?:(?:thermals_r7|thermals_r9|sites|hazards|skyways)\\.(?:parquet|geojson)|README\\.md|LICENSE\\.txt|manifest\\.json)$",
              "description": "A file the manifest lists",
              "title": "File"
            },
            "description": "A file the manifest lists"
          }
        ],
        "responses": {
          "200": {
            "description": "The file (folder stores)",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "307": {
            "description": "Presigned object store URL, valid 10 minutes (S3 stores)"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `open_data_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/regions": {
      "get": {
        "tags": [
          "regions"
        ],
        "summary": "Public flying regions with simplified boundaries",
        "operationId": "list_regions_v1_regions_get",
        "responses": {
          "200": {
            "description": "GeoJSON FeatureCollection ordered by slug; `ETag` and `Cache-Control` headers",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/RegionCollection"
                }
              }
            }
          },
          "304": {
            "description": "`If-None-Match` matched the current `ETag`; no body"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/geo+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "APIKeyHeader": []
          }
        ]
      }
    },
    "/v1/regions/{region}/profile": {
      "get": {
        "tags": [
          "regions"
        ],
        "summary": "Profile of a public flying region",
        "operationId": "region_profile_v1_regions__region__profile_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "region",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
              "description": "Region id (uuid, lowercase) or slug (`regions.slug`), e.g. `fethiye-oludeniz`",
              "title": "Region"
            },
            "description": "Region id (uuid, lowercase) or slug (`regions.slug`), e.g. `fethiye-oludeniz`"
          }
        ],
        "responses": {
          "200": {
            "description": "Region profile; `ETag` and `Cache-Control` headers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegionProfileResponse"
                }
              }
            }
          },
          "304": {
            "description": "`If-None-Match` matched the current `ETag`; no body"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `region_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/regions/{region}/incidents": {
      "get": {
        "tags": [
          "regions"
        ],
        "summary": "Known incidents and precautions of a public flying region",
        "description": "Entered by the region's moderators. Not in-flight information: use it for planning and briefing. No person, no day, no ranking.",
        "operationId": "region_incidents_v1_regions__region__incidents_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "region",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
              "description": "Region id (uuid, lowercase) or slug (`regions.slug`)",
              "title": "Region"
            },
            "description": "Region id (uuid, lowercase) or slug (`regions.slug`)"
          }
        ],
        "responses": {
          "200": {
            "description": "Incidents; `ETag` and `Cache-Control` headers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegionIncidentsResponse"
                }
              }
            }
          },
          "304": {
            "description": "`If-None-Match` matched the current `ETag`; no body"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `region_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/consents/withdrawals": {
      "post": {
        "tags": [
          "consents"
        ],
        "summary": "Withdraw a pilot's consent to the shared layers",
        "description": "Pilot-wide (no `flight_ids`): forward-looking, the flights uploaded from now on do\nnot enter the shared layers until a pilot-wide grant; earlier flights stay in the\nanonymous aggregates. With `flight_ids`: those flights leave the shared layers, at\nonce for new layer computations, within 24 hours in every published grid cell and\ntake-off/landing count. Raw IGC and fixes stay (withdrawal is not deletion).\nRepeating the request is safe.",
        "operationId": "withdraw_consent_v1_consents_withdrawals_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsentReceipt"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `flight_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "APIKeyHeader": []
          }
        ]
      }
    },
    "/v1/consents/grants": {
      "post": {
        "tags": [
          "consents"
        ],
        "summary": "Give consent to the shared layers again",
        "description": "Re-consent after a withdrawal: the flights (those passing the quality gate) count\nagain after the consent sync, which this request queues; a pilot-wide grant also\nopens the flights uploaded while the pilot was withdrawn (not the ones it left out by\nlist). Repeating it is safe.",
        "operationId": "grant_consent_v1_consents_grants_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConsentReceipt"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `flight_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `queue_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "APIKeyHeader": []
          }
        ]
      }
    },
    "/v1/flights/{flight_id}/shares": {
      "post": {
        "tags": [
          "shares"
        ],
        "summary": "Share a flight: open (or return) its public replay link",
        "description": "201 with a new link, or 200 with the active one when the flight is already shared\n(repeating the request is safe). The link works until the pilot stops sharing or the\nflight is deleted; sharing again after that gives a new link.",
        "operationId": "open_share_v1_flights__flight_id__shares_post",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "flight_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Updraft flight id (`flight_id` of the upload response)",
              "title": "Flight Id"
            },
            "description": "Updraft flight id (`flight_id` of the upload response)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The flight was already shared: same link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareOut"
                }
              }
            }
          },
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareOut"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `flight_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "code: `flight_deleted`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "shares"
        ],
        "summary": "Stop sharing a flight: its public link is gone at once",
        "description": "The link answers 404 from now on (public caches drop it within the replay's\n`max-age`, §12). 204 also when nothing was shared (repeating the request is safe).",
        "operationId": "close_share_v1_flights__flight_id__shares_delete",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "flight_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Updraft flight id (`flight_id` of the upload response)",
              "title": "Flight Id"
            },
            "description": "Updraft flight id (`flight_id` of the upload response)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareRequestBody"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `flight_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/deletions": {
      "post": {
        "tags": [
          "deletions"
        ],
        "summary": "Delete a pilot's flights, or the pilot's data altogether (KVKK)",
        "description": "With `flight_ids`: those flights. Pilot-wide (no `flight_ids`): every flight of the\npilot, the pilot's privacy masks and the link to the client's pilot id. At once: the\nflights leave every API answer and every new layer computation and their public\nshare links close; within minutes (at the latest the next night) the raw IGC, fix\nfiles, videos and thermal events are deleted; within 24 hours every published grid\ncell and take-off/landing count is recomputed without them. The same file uploaded\nagain is refused (409 `flight_deleted`). Repeating the request is safe.",
        "operationId": "create_deletion_v1_deletions_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeletionRequestBody"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletionReceiptOut"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `flight_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        },
        "security": [
          {
            "APIKeyHeader": []
          }
        ]
      }
    },
    "/v1/deletions/{deletion_id}": {
      "get": {
        "tags": [
          "deletions"
        ],
        "summary": "Progress of a deletion request",
        "description": "`pending` until the files of every flight are gone; then `completed`.",
        "operationId": "get_deletion_v1_deletions__deletion_id__get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "deletion_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "`deletion_id` of the request",
              "title": "Deletion Id"
            },
            "description": "`deletion_id` of the request"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeletionStatusOut"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `deletion_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/privacy-masks": {
      "post": {
        "tags": [
          "privacy-masks"
        ],
        "summary": "Add a home or custom privacy mask for a pilot",
        "description": "The pilot's fixes inside the mask leave every shared layer: within minutes the\npilot's flights are processed again with it, within 24 hours every published grid\ncell, thermal, take-off/landing and skyway count is recomputed without them. The public\nreplay of a shared flight hides the area at once. An unknown pilot is created, so a\nmask set before the first upload covers it.",
        "operationId": "create_mask_v1_privacy_masks_post",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PilotMaskCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PilotMaskReceipt"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "409": {
            "description": "code: `home_mask_exists`, `mask_limit`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `mask_invalid`, `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "privacy-masks"
        ],
        "summary": "The privacy masks of a pilot",
        "description": "The pilot's masks with their geometry, oldest first; an unknown pilot has none. The\npilot is named in the `X-Pilot-Hash` header, not in the address.",
        "operationId": "get_masks_v1_privacy_masks_get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "X-Pilot-Hash",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{64}$",
              "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads",
              "title": "X-Pilot-Hash"
            },
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PilotMaskList"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/privacy-masks/{mask_id}": {
      "delete": {
        "tags": [
          "privacy-masks"
        ],
        "summary": "Remove a privacy mask of a pilot",
        "description": "The pilot's flights are processed again without the mask; its fixes may then count\nin the shared layers again (threshold rules unchanged).",
        "operationId": "delete_mask_v1_privacy_masks__mask_id__delete",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "mask_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "`id` of the mask",
              "title": "Mask Id"
            },
            "description": "`id` of the mask"
          },
          {
            "name": "X-Pilot-Hash",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{64}$",
              "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads",
              "title": "X-Pilot-Hash"
            },
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PilotMaskReceipt"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `mask_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/replay/{public_id}": {
      "get": {
        "tags": [
          "replay"
        ],
        "summary": "Public replay of a shared flight",
        "description": "Fixes and thermal events of a flight its pilot shared. Masked and unusable fixes are left out (a masked stretch splits the track); times are relative; at most `max_points` points. Unknown, revoked and deleted shares are the same 404.",
        "operationId": "public_replay_v1_replay__public_id__get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "public_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "`public_id` of the share (the link)",
              "title": "Public Id"
            },
            "description": "`public_id` of the share (the link)"
          },
          {
            "name": "max_points",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 5000,
              "minimum": 100,
              "description": "Thinning limit",
              "default": 5000,
              "title": "Max Points"
            },
            "description": "Thinning limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Public replay; `ETag` and `Cache-Control: public` headers",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicReplay"
                }
              }
            }
          },
          "304": {
            "description": "`If-None-Match` matched the current `ETag`; no body"
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `replay_not_found`, `replay_not_ready`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/replay/{public_id}/video": {
      "post": {
        "tags": [
          "replay"
        ],
        "summary": "Ask for the MP4 video of a shared flight",
        "description": "Queues a video of the shared flight (the same public replay as the Atlas page) in the asked format and length, or returns the one already queued, rendering or ready. Made once per share, format and length; after a failure, asking again queues it again. On the server without a graphics card a 15 s video (the default) is ready in about 3.5 minutes, a 30 s one in about 7: show the pilot that it is being prepared and poll `status_path`.",
        "operationId": "request_video_v1_replay__public_id__video_post",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "public_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "`public_id` of the share (the link)",
              "title": "Public Id"
            },
            "description": "`public_id` of the share (the link)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The video is ready (`url` set)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoInfo"
                }
              }
            }
          },
          "202": {
            "description": "Queued or rendering; poll `status_path`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoInfo"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `replay_not_found`, `replay_not_ready`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `queue_unavailable`, `video_download_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/replay/{public_id}/video/{video_format}": {
      "get": {
        "tags": [
          "replay"
        ],
        "summary": "State of a video, with a download URL when ready",
        "operationId": "get_video_v1_replay__public_id__video__video_format__get",
        "security": [
          {
            "APIKeyHeader": []
          }
        ],
        "parameters": [
          {
            "name": "public_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "`public_id` of the share (the link)",
              "title": "Public Id"
            },
            "description": "`public_id` of the share (the link)"
          },
          {
            "name": "video_format",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/VideoFormat",
              "description": "`16x9` (1920x1080) or `9x16` (1080x1920)"
            },
            "description": "`16x9` (1920x1080) or `9x16` (1080x1920)"
          },
          {
            "name": "length_s",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/VideoLength",
              "description": "Longest video, seconds, as asked for with POST: `15` (default) or `30`",
              "default": 15
            },
            "description": "Longest video, seconds, as asked for with POST: `15` (default) or `30`"
          }
        ],
        "responses": {
          "200": {
            "description": "`queued`, `rendering`, `ready` (with `url`) or `failed`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoInfo"
                }
              }
            }
          },
          "401": {
            "description": "code: `auth_missing`, `auth_invalid`, `auth_revoked`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "403": {
            "description": "code: `auth_scope`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `replay_not_found`, `replay_not_ready`, `video_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "429": {
            "description": "code: `rate_limited`, `quota_exceeded` (`Retry-After` seconds)",
            "headers": {
              "Retry-After": {
                "description": "Seconds until the exhausted window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `queue_unavailable`, `video_download_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    },
    "/v1/replay/{public_id}/video/{video_format}/file": {
      "get": {
        "tags": [
          "replay"
        ],
        "summary": "Download a video through a signed URL (no API key)",
        "operationId": "get_video_file_v1_replay__public_id__video__video_format__file_get",
        "parameters": [
          {
            "name": "public_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "`public_id` of the share (the link)",
              "title": "Public Id"
            },
            "description": "`public_id` of the share (the link)"
          },
          {
            "name": "video_format",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/VideoFormat",
              "description": "`16x9` (1920x1080) or `9x16` (1080x1920)"
            },
            "description": "`16x9` (1920x1080) or `9x16` (1080x1920)"
          },
          {
            "name": "expires",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "description": "Unix seconds, from the signed URL",
              "title": "Expires"
            },
            "description": "Unix seconds, from the signed URL"
          },
          {
            "name": "signature",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "description": "From the signed URL",
              "title": "Signature"
            },
            "description": "From the signed URL"
          },
          {
            "name": "length_s",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/VideoLength",
              "description": "Longest video, seconds, as asked for with POST: `15` (default) or `30`",
              "default": 15
            },
            "description": "Longest video, seconds, as asked for with POST: `15` (default) or `30`"
          }
        ],
        "responses": {
          "200": {
            "description": "The MP4 (`Range` requests answer 206 on a folder store)",
            "content": {
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "code: `video_url_invalid`, `video_url_expired`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "404": {
            "description": "code: `replay_not_found`, `video_not_found`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "422": {
            "description": "code: `request_invalid`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          },
          "503": {
            "description": "code: `video_download_unavailable`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorBody"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Body_upload_flight_v1_flights_post": {
        "properties": {
          "file": {
            "type": "string",
            "contentMediaType": "application/octet-stream",
            "title": "File",
            "description": "IGC, GPX or FIT file, byte for byte as recorded; the format is detected from the content"
          },
          "external_pilot_hash": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{64}$",
            "title": "External Pilot Hash",
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the HMAC key stays with the client"
          },
          "consent_version": {
            "type": "integer",
            "maximum": 32767,
            "minimum": 0,
            "title": "Consent Version",
            "description": "Version of the consent text the pilot accepted"
          },
          "source": {
            "type": "string",
            "const": "kunduzo",
            "title": "Source",
            "description": "Uploading platform"
          },
          "external_ref": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200,
                "minLength": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "External Ref",
            "description": "Client's own flight reference"
          },
          "sport": {
            "$ref": "#/components/schemas/Sport",
            "description": "Sport of the flight (optional, default `paragliding`); stored with a new flight. Shared layers count only `paragliding` flights for now",
            "default": "paragliding"
          }
        },
        "type": "object",
        "required": [
          "file",
          "external_pilot_hash",
          "consent_version",
          "source"
        ],
        "title": "Body_upload_flight_v1_flights_post"
      },
      "ConsentReceipt": {
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "withdrawn",
              "granted"
            ],
            "title": "Event"
          },
          "scope": {
            "type": "string",
            "enum": [
              "pilot",
              "flights"
            ],
            "title": "Scope"
          },
          "recorded": {
            "type": "boolean",
            "title": "Recorded",
            "description": "A new consent event was written; `false`: it was already in effect (repeated request), nothing changed"
          },
          "flights_in_scope": {
            "type": "integer",
            "title": "Flights In Scope",
            "description": "Flights the request covers (deleted ones aside); 0 for a pilot-wide withdrawal, which only covers later uploads"
          },
          "flights_affected": {
            "type": "integer",
            "title": "Flights Affected",
            "description": "Withdrawal by list: flights that counted towards the shared layers and no longer do; 0 for a pilot-wide withdrawal. Grant: flights not in the layers now that are checked again (consent and quality)"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "title": "Received At",
            "description": "Time the request was recorded (UTC)"
          },
          "layers_updated_by": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Layers Updated By",
            "description": "Withdrawal by list: the layers no longer contain these flights by this time (received_at + 24 h); null for a pilot-wide withdrawal (no flight leaves the layers) and for a grant"
          }
        },
        "type": "object",
        "required": [
          "event",
          "scope",
          "recorded",
          "flights_in_scope",
          "flights_affected",
          "received_at",
          "layers_updated_by"
        ],
        "title": "ConsentReceipt",
        "description": "What the request changed. No pilot identity is returned."
      },
      "ConsentRequest": {
        "properties": {
          "external_pilot_hash": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{64}$",
            "title": "External Pilot Hash",
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads"
          },
          "consent_version": {
            "type": "integer",
            "maximum": 32767,
            "minimum": 0,
            "title": "Consent Version",
            "description": "Version of the consent text the pilot acted on"
          },
          "source": {
            "type": "string",
            "const": "kunduzo",
            "title": "Source",
            "description": "Platform the pilot gave or withdrew it on"
          },
          "flight_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string",
                  "format": "uuid"
                },
                "type": "array",
                "maxItems": 1000,
                "minItems": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Flight Ids",
            "description": "Updraft flight ids (`flight_id` of the upload response) of this pilot. Omitted: pilot-wide; a pilot-wide withdrawal covers the flights uploaded from now on (earlier flights stay in the anonymous aggregates), a pilot-wide grant every flight"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "external_pilot_hash",
          "consent_version",
          "source"
        ],
        "title": "ConsentRequest",
        "description": "Pilot-wide (no `flight_ids`) or for the listed flights of that pilot."
      },
      "DeletionReceiptOut": {
        "properties": {
          "deletion_id": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "type": "null"
              }
            ],
            "title": "Deletion Id",
            "description": "The deletion request covering this one (`GET /v1/deletions/{id}`): new when `recorded`, else the earlier request that already covers it; null when there is nothing to delete (pilot unknown to Updraft or already erased)"
          },
          "scope": {
            "type": "string",
            "enum": [
              "pilot",
              "flights"
            ],
            "title": "Scope"
          },
          "recorded": {
            "type": "boolean",
            "title": "Recorded",
            "description": "A new deletion was recorded; `false`: everything asked was already deleted or being deleted (repeated request), nothing changed"
          },
          "flights_in_scope": {
            "type": "integer",
            "title": "Flights In Scope",
            "description": "With `flight_ids`: the listed flights; pilot-wide: the flights of the covering request"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed"
            ],
            "title": "Status",
            "description": "`completed`: raw files, fix files and thermal events are gone"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "title": "Received At",
            "description": "When the covering request was received (UTC)"
          },
          "layers_updated_by": {
            "type": "string",
            "format": "date-time",
            "title": "Layers Updated By",
            "description": "The published layers no longer contain these flights by this time (received_at + 24 h)"
          }
        },
        "type": "object",
        "required": [
          "deletion_id",
          "scope",
          "recorded",
          "flights_in_scope",
          "status",
          "received_at",
          "layers_updated_by"
        ],
        "title": "DeletionReceiptOut",
        "description": "What the request recorded. No pilot identity and no flight id is returned."
      },
      "DeletionRequestBody": {
        "properties": {
          "external_pilot_hash": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{64}$",
            "title": "External Pilot Hash",
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads"
          },
          "source": {
            "type": "string",
            "const": "kunduzo",
            "title": "Source",
            "description": "Platform the pilot asked on"
          },
          "flight_ids": {
            "anyOf": [
              {
                "items": {
                  "type": "string",
                  "format": "uuid"
                },
                "type": "array",
                "maxItems": 1000,
                "minItems": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Flight Ids",
            "description": "Updraft flight ids (`flight_id` of the upload response) of this pilot. Omitted: pilot-wide, every flight of the pilot and the pilot's link to the client's pilot id"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "external_pilot_hash",
          "source"
        ],
        "title": "DeletionRequestBody",
        "description": "Pilot-wide (no `flight_ids`) or the listed flights of that pilot."
      },
      "DeletionStatusOut": {
        "properties": {
          "deletion_id": {
            "type": "string",
            "format": "uuid",
            "title": "Deletion Id"
          },
          "scope": {
            "type": "string",
            "enum": [
              "pilot",
              "flights"
            ],
            "title": "Scope"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed"
            ],
            "title": "Status"
          },
          "flights": {
            "type": "integer",
            "title": "Flights",
            "description": "Flights the request deletes"
          },
          "flights_completed": {
            "type": "integer",
            "title": "Flights Completed",
            "description": "Of these, flights whose files are gone"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "title": "Received At"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Completed At",
            "description": "Every flight done (pilot-wide: also the pilot's masks and link); null while pending"
          },
          "layers_updated_by": {
            "type": "string",
            "format": "date-time",
            "title": "Layers Updated By",
            "description": "received_at + 24 h"
          }
        },
        "type": "object",
        "required": [
          "deletion_id",
          "scope",
          "status",
          "flights",
          "flights_completed",
          "received_at",
          "completed_at",
          "layers_updated_by"
        ],
        "title": "DeletionStatusOut",
        "description": "Progress of one deletion request. No pilot identity and no flight id."
      },
      "ErrorBody": {
        "properties": {
          "code": {
            "type": "string",
            "title": "Code"
          },
          "detail": {
            "type": "string",
            "title": "Detail"
          },
          "errors": {
            "anyOf": [
              {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Errors"
          }
        },
        "type": "object",
        "required": [
          "code",
          "detail"
        ],
        "title": "ErrorBody"
      },
      "FlightReceipt": {
        "properties": {
          "flight_id": {
            "type": "string",
            "format": "uuid",
            "title": "Flight Id",
            "description": "uuid5 of the file's SHA-256; same bytes, same id"
          },
          "status": {
            "type": "string",
            "enum": [
              "accepted",
              "duplicate"
            ],
            "title": "Status",
            "description": "`accepted` (202): new flight, queued; `duplicate` (200): already stored"
          },
          "igc_sha256": {
            "type": "string",
            "title": "Igc Sha256",
            "description": "SHA-256 of the uploaded bytes (IGC, GPX or FIT), lower case hex"
          },
          "flown_on": {
            "type": "string",
            "format": "date",
            "title": "Flown On",
            "description": "Local (Europe/Istanbul) date of the first valid fix"
          },
          "consent_version": {
            "type": "integer",
            "title": "Consent Version",
            "description": "Consent text version stored with the flight"
          },
          "sport": {
            "$ref": "#/components/schemas/Sport",
            "description": "Sport stored with the flight; a `duplicate` keeps the sport of the first upload. Shared layers count only `paragliding` flights for now"
          },
          "processing": {
            "type": "string",
            "enum": [
              "pending",
              "processed",
              "failed"
            ],
            "title": "Processing"
          },
          "usable_for_layers": {
            "type": "boolean",
            "title": "Usable For Layers",
            "description": "Counts towards shared layers (consent and quality); false until processed"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "title": "Received At",
            "description": "Time this upload was received (UTC)"
          }
        },
        "type": "object",
        "required": [
          "flight_id",
          "status",
          "igc_sha256",
          "flown_on",
          "consent_version",
          "sport",
          "processing",
          "usable_for_layers",
          "received_at"
        ],
        "title": "FlightReceipt",
        "description": "The stored flight. Pilot identity, track and altitude are never returned."
      },
      "ForecastCell": {
        "properties": {
          "lat": {
            "type": "number",
            "title": "Lat",
            "description": "Latitude of the 0.25° cell centre the forecast is for"
          },
          "lon": {
            "type": "number",
            "title": "Lon",
            "description": "Longitude of the cell centre"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "lat",
          "lon"
        ],
        "title": "ForecastCell"
      },
      "ForecastInfo": {
        "properties": {
          "source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Source",
            "description": "Forecast service (`open-meteo`); null when off"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Model",
            "description": "Weather model (`icon_eu`); null when off"
          },
          "cell": {
            "$ref": "#/components/schemas/ForecastCell"
          },
          "fetched_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fetched At",
            "description": "When our forecast server produced this forecast (UTC); the model run is at most about 9 hours older. Null without a forecast"
          },
          "attribution": {
            "type": "string",
            "title": "Attribution",
            "description": "Required data attribution (CC BY 4.0)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "source",
          "model",
          "cell",
          "fetched_at",
          "attribution"
        ],
        "title": "ForecastInfo"
      },
      "Health": {
        "properties": {
          "status": {
            "type": "string",
            "const": "ok",
            "title": "Status"
          },
          "version": {
            "type": "string",
            "title": "Version"
          }
        },
        "type": "object",
        "required": [
          "status",
          "version"
        ],
        "title": "Health"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "IncidentRegion": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id"
          },
          "slug": {
            "type": "string",
            "title": "Slug"
          },
          "name": {
            "type": "string",
            "title": "Name"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "slug",
          "name"
        ],
        "title": "IncidentRegion"
      },
      "IncidentType": {
        "type": "string",
        "enum": [
          "collision_obstacle",
          "takeoff_problem",
          "landing_problem",
          "rotor_turbulence",
          "power_line",
          "airspace",
          "other"
        ],
        "title": "IncidentType",
        "description": "docs/02 features; the same list as migration 0013 (a test compares)."
      },
      "MultiPolygonGeometry": {
        "properties": {
          "type": {
            "type": "string",
            "const": "MultiPolygon",
            "title": "Type"
          },
          "coordinates": {
            "items": {
              "items": {
                "items": {
                  "prefixItems": [
                    {
                      "type": "number"
                    },
                    {
                      "type": "number"
                    }
                  ],
                  "type": "array",
                  "maxItems": 2,
                  "minItems": 2
                },
                "type": "array"
              },
              "type": "array"
            },
            "type": "array",
            "title": "Coordinates",
            "description": "Polygons → rings → [longitude, latitude] (WGS 84), simplified"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "coordinates"
        ],
        "title": "MultiPolygonGeometry"
      },
      "NoticeText": {
        "properties": {
          "tr": {
            "type": "string",
            "title": "Tr"
          },
          "en": {
            "type": "string",
            "title": "En"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "tr",
          "en"
        ],
        "title": "NoticeText"
      },
      "OpenDataAttribution": {
        "properties": {
          "tr": {
            "type": "string",
            "title": "Tr"
          },
          "en": {
            "type": "string",
            "title": "En"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "tr",
          "en"
        ],
        "title": "OpenDataAttribution"
      },
      "OpenDataFileInfo": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name",
            "description": "File name, e.g. `sites.geojson`"
          },
          "layer": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Layer",
            "description": "`thermals_r7`, `thermals_r9`, `sites`, `hazards`, `skyways`; null for README.md and LICENSE.txt"
          },
          "format": {
            "type": "string",
            "title": "Format",
            "description": "`geoparquet` (GeoParquet 1.1), `geojson`, `markdown` (README), `text` (LICENSE)"
          },
          "rows": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Rows",
            "description": "Features in the layer; null for documents"
          },
          "size_bytes": {
            "type": "integer",
            "title": "Size Bytes"
          },
          "sha256": {
            "type": "string",
            "title": "Sha256",
            "description": "SHA-256 of the file, 64 lowercase hex characters"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "name",
          "layer",
          "format",
          "rows",
          "size_bytes",
          "sha256"
        ],
        "title": "OpenDataFileInfo"
      },
      "OpenDataList": {
        "properties": {
          "releases": {
            "items": {
              "$ref": "#/components/schemas/OpenDataReleaseInfo"
            },
            "type": "array",
            "title": "Releases",
            "description": "Newest year first; may be empty"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "releases"
        ],
        "title": "OpenDataList"
      },
      "OpenDataReleaseInfo": {
        "properties": {
          "year": {
            "type": "integer",
            "title": "Year",
            "description": "Release year (the content is the build day's cumulative state)"
          },
          "generated_on": {
            "type": "string",
            "format": "date",
            "title": "Generated On",
            "description": "UTC day the package was built"
          },
          "open_data_version": {
            "type": "integer",
            "title": "Open Data Version",
            "description": "Package format version"
          },
          "license_id": {
            "type": "string",
            "title": "License Id",
            "description": "`CC-BY-4.0`"
          },
          "license_url": {
            "type": "string",
            "title": "License Url"
          },
          "attribution": {
            "$ref": "#/components/schemas/OpenDataAttribution",
            "description": "Required credit line (docs/29 §2)"
          },
          "thresholds": {
            "$ref": "#/components/schemas/OpenDataThresholds",
            "description": "Privacy thresholds at build time"
          },
          "files": {
            "items": {
              "$ref": "#/components/schemas/OpenDataFileInfo"
            },
            "type": "array",
            "title": "Files",
            "description": "Package files (manifest.json not listed)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "year",
          "generated_on",
          "open_data_version",
          "license_id",
          "license_url",
          "attribution",
          "thresholds",
          "files"
        ],
        "title": "OpenDataReleaseInfo"
      },
      "OpenDataThresholds": {
        "properties": {
          "min_pilots_grid": {
            "type": "integer",
            "title": "Min Pilots Grid",
            "description": "Distinct pilots behind every thermal cell"
          },
          "min_pilots_features": {
            "type": "integer",
            "title": "Min Pilots Features",
            "description": "Distinct pilots behind every site and hazard"
          },
          "min_pilots_skyways": {
            "type": "integer",
            "title": "Min Pilots Skyways",
            "description": "Distinct pilots behind every skyway"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "min_pilots_grid",
          "min_pilots_features",
          "min_pilots_skyways"
        ],
        "title": "OpenDataThresholds"
      },
      "PackFileInfo": {
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "base",
              "contours",
              "terrain",
              "hillshade",
              "power"
            ],
            "title": "Kind",
            "description": "Base map source the file replaces offline (docs/02 §6)"
          },
          "file": {
            "type": "string",
            "title": "File",
            "description": "File name, `{kind}.pmtiles`"
          },
          "url": {
            "type": "string",
            "title": "Url",
            "description": "Time-limited GET URL (until `expires_at`); supports HTTP range requests"
          },
          "size_bytes": {
            "type": "integer",
            "title": "Size Bytes"
          },
          "sha256": {
            "type": "string",
            "title": "Sha256",
            "description": "SHA-256 of the file, 64 lowercase hex characters"
          },
          "tile_type": {
            "type": "string",
            "enum": [
              "mvt",
              "webp"
            ],
            "title": "Tile Type"
          },
          "tile_compression": {
            "type": "string",
            "enum": [
              "gzip",
              "none"
            ],
            "title": "Tile Compression"
          },
          "min_zoom": {
            "type": "integer",
            "title": "Min Zoom"
          },
          "max_zoom": {
            "type": "integer",
            "title": "Max Zoom",
            "description": "Highest zoom in the file; MapLibre over-zooms beyond it"
          },
          "tile_count": {
            "type": "integer",
            "title": "Tile Count"
          },
          "source_build_id": {
            "type": "string",
            "title": "Source Build Id",
            "description": "Base map build the file was cut from"
          },
          "attribution": {
            "type": "string",
            "title": "Attribution",
            "description": "Attribution of the source (may hold HTML links)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "kind",
          "file",
          "url",
          "size_bytes",
          "sha256",
          "tile_type",
          "tile_compression",
          "min_zoom",
          "max_zoom",
          "tile_count",
          "source_build_id",
          "attribution"
        ],
        "title": "PackFileInfo",
        "description": "One PMTiles file; a MapLibre source of the same name reads it as `pmtiles://`."
      },
      "PackInfo": {
        "properties": {
          "region": {
            "type": "string",
            "title": "Region"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Region name when cut from `regions`"
          },
          "pack_version": {
            "type": "string",
            "title": "Pack Version",
            "description": "`YYYYMMDD[-n]`; a larger value is a newer pack. Compare it with the downloaded one to decide whether to download again"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          },
          "bbox": {
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ],
            "type": "array",
            "maxItems": 4,
            "minItems": 4,
            "title": "Bbox",
            "description": "[west, south, east, north] degrees, WGS 84: every tile meeting it is in"
          },
          "size_bytes": {
            "type": "integer",
            "title": "Size Bytes",
            "description": "Sum of the files"
          },
          "max_zoom": {
            "type": "integer",
            "title": "Max Zoom"
          },
          "zoom_trimmed": {
            "type": "boolean",
            "title": "Zoom Trimmed",
            "description": "True when zoom levels were dropped to stay under the size limit"
          },
          "files": {
            "items": {
              "$ref": "#/components/schemas/PackFileInfo"
            },
            "type": "array",
            "title": "Files"
          },
          "attribution": {
            "type": "string",
            "title": "Attribution",
            "description": "Credit line to show with the offline map"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "title": "Expires At",
            "description": "The file URLs stop working at this time (UTC)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "region",
          "name",
          "pack_version",
          "created_at",
          "bbox",
          "size_bytes",
          "max_zoom",
          "zoom_trimmed",
          "files",
          "attribution",
          "expires_at"
        ],
        "title": "PackInfo"
      },
      "PilotMaskCreate": {
        "properties": {
          "external_pilot_hash": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{64}$",
            "title": "External Pilot Hash",
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the uploads"
          },
          "source": {
            "type": "string",
            "const": "kunduzo",
            "title": "Source",
            "description": "Platform the pilot set the mask on"
          },
          "kind": {
            "type": "string",
            "enum": [
              "home",
              "custom"
            ],
            "title": "Kind",
            "description": "`home`: the pilot's home (at most one per pilot); `custom`: another place"
          },
          "center": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PilotMaskPoint"
              },
              {
                "type": "null"
              }
            ],
            "description": "Centre of a circle"
          },
          "radius_m": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 10000,
                "minimum": 1
              },
              {
                "type": "null"
              }
            ],
            "title": "Radius M",
            "description": "Circle radius in metres. `home`: at least the home mask default (privacy settings, 2000) and the default when omitted; `custom`: 200-10000, required with `center`"
          },
          "polygon": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PilotMaskPolygonIn"
              },
              {
                "type": "null"
              }
            ],
            "description": "`custom` only, instead of a circle; area between a 200 m and a 10000 m circle's"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "external_pilot_hash",
          "source",
          "kind"
        ],
        "title": "PilotMaskCreate",
        "description": "`home`: a circle (`center`, `radius_m` optional). `custom`: a circle (`center` +\n`radius_m`) or a `polygon`."
      },
      "PilotMaskList": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PilotMaskOut"
            },
            "type": "array",
            "title": "Items",
            "description": "The pilot's masks, oldest first"
          },
          "max_masks": {
            "type": "integer",
            "title": "Max Masks",
            "description": "Masks a pilot may have"
          }
        },
        "type": "object",
        "required": [
          "items",
          "max_masks"
        ],
        "title": "PilotMaskList"
      },
      "PilotMaskOut": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id"
          },
          "kind": {
            "type": "string",
            "enum": [
              "home",
              "custom"
            ],
            "title": "Kind"
          },
          "center": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PilotMaskPoint"
              },
              {
                "type": "null"
              }
            ],
            "description": "`home`: the circle centre; `custom`: null"
          },
          "radius_m": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Radius M",
            "description": "`home`: the circle radius; `custom`: null"
          },
          "polygon": {
            "$ref": "#/components/schemas/PilotMaskPolygon"
          },
          "area_km2": {
            "type": "number",
            "title": "Area Km2",
            "description": "Area of the stored geometry, 3 decimals"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          }
        },
        "type": "object",
        "required": [
          "id",
          "kind",
          "center",
          "radius_m",
          "polygon",
          "area_km2",
          "created_at"
        ],
        "title": "PilotMaskOut",
        "description": "One mask of the pilot, with its geometry (for the client's own pilot only)."
      },
      "PilotMaskPoint": {
        "properties": {
          "lat": {
            "type": "number",
            "maximum": 90,
            "minimum": -90,
            "title": "Lat",
            "description": "Latitude (WGS84, degrees)"
          },
          "lon": {
            "type": "number",
            "maximum": 180,
            "minimum": -180,
            "title": "Lon",
            "description": "Longitude (WGS84, degrees)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "lat",
          "lon"
        ],
        "title": "PilotMaskPoint"
      },
      "PilotMaskPolygon": {
        "properties": {
          "type": {
            "type": "string",
            "const": "Polygon",
            "title": "Type"
          },
          "coordinates": {
            "items": {
              "items": {
                "items": {
                  "type": "number"
                },
                "type": "array"
              },
              "type": "array"
            },
            "type": "array",
            "title": "Coordinates",
            "description": "The stored geometry, `[lon, lat]` with 6 decimals (a circle as its polygon)"
          }
        },
        "type": "object",
        "required": [
          "type",
          "coordinates"
        ],
        "title": "PilotMaskPolygon"
      },
      "PilotMaskPolygonIn": {
        "properties": {
          "type": {
            "type": "string",
            "const": "Polygon",
            "title": "Type"
          },
          "coordinates": {
            "items": {
              "items": {
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ],
                "type": "array",
                "maxItems": 2,
                "minItems": 2
              },
              "type": "array"
            },
            "type": "array",
            "maxItems": 1,
            "minItems": 1,
            "title": "Coordinates",
            "description": "One closed ring of 4-500 `[lon, lat]` positions (last = first); a valid, non self-intersecting polygon"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "coordinates"
        ],
        "title": "PilotMaskPolygonIn",
        "description": "GeoJSON Polygon with one ring (no holes), positions `[lon, lat]`."
      },
      "PilotMaskReceipt": {
        "properties": {
          "mask": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PilotMaskOut"
              },
              {
                "type": "null"
              }
            ],
            "description": "The new mask; null after a removal"
          },
          "removed_id": {
            "anyOf": [
              {
                "type": "string",
                "format": "uuid"
              },
              {
                "type": "null"
              }
            ],
            "title": "Removed Id",
            "description": "The removed mask; null after an addition"
          },
          "flights_affected": {
            "type": "integer",
            "title": "Flights Affected",
            "description": "The pilot's flights that are processed again with the changed masks"
          },
          "received_at": {
            "type": "string",
            "format": "date-time",
            "title": "Received At",
            "description": "Time the change was recorded (UTC)"
          },
          "layers_updated_by": {
            "type": "string",
            "format": "date-time",
            "title": "Layers Updated By",
            "description": "The published layers follow the change by this time (received_at + 24 h)"
          }
        },
        "type": "object",
        "required": [
          "mask",
          "removed_id",
          "flights_affected",
          "received_at",
          "layers_updated_by"
        ],
        "title": "PilotMaskReceipt",
        "description": "What the change recorded. No pilot identity is returned."
      },
      "PointGeometry": {
        "properties": {
          "type": {
            "type": "string",
            "const": "Point",
            "title": "Type"
          },
          "coordinates": {
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ],
            "type": "array",
            "maxItems": 2,
            "minItems": 2,
            "title": "Coordinates",
            "description": "[longitude, latitude], WGS 84"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "coordinates"
        ],
        "title": "PointGeometry"
      },
      "PublicIncident": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id",
            "description": "Incident id (stable while the record exists)"
          },
          "type": {
            "$ref": "#/components/schemas/IncidentType",
            "description": "Kind of incident (fixed list, docs/02 features)"
          },
          "period_from": {
            "type": "string",
            "pattern": "^[0-9]{4}(-(0[1-9]|1[0-2]))?$",
            "title": "Period From",
            "description": "Start of the period: `YYYY` or `YYYY-MM` (never a day)"
          },
          "period_to": {
            "type": "string",
            "pattern": "^[0-9]{4}(-(0[1-9]|1[0-2]))?$",
            "title": "Period To",
            "description": "End of the period: `YYYY` or `YYYY-MM`"
          },
          "precaution": {
            "type": "string",
            "maxLength": 280,
            "title": "Precaution",
            "description": "What to do or avoid here (moderator's note)"
          },
          "lon": {
            "type": "number",
            "maximum": 180,
            "minimum": -180,
            "title": "Lon",
            "description": "Longitude, WGS 84, 5 decimals"
          },
          "lat": {
            "type": "number",
            "maximum": 90,
            "minimum": -90,
            "title": "Lat",
            "description": "Latitude, WGS 84, 5 decimals"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "type",
          "period_from",
          "period_to",
          "precaution",
          "lon",
          "lat"
        ],
        "title": "PublicIncident"
      },
      "PublicReplay": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id",
            "description": "The share's `public_id`"
          },
          "summary": {
            "$ref": "#/components/schemas/PublicReplaySummary"
          },
          "parts": {
            "items": {
              "$ref": "#/components/schemas/PublicReplayPart"
            },
            "type": "array",
            "title": "Parts",
            "description": "Time ordered; a masked stretch between two parts is not returned"
          },
          "thermals": {
            "items": {
              "$ref": "#/components/schemas/PublicReplayThermal"
            },
            "type": "array",
            "title": "Thermals",
            "description": "Thermal events starting inside a part; empty when none"
          },
          "max_points": {
            "type": "integer",
            "title": "Max Points"
          }
        },
        "type": "object",
        "required": [
          "id",
          "summary",
          "parts",
          "thermals",
          "max_points"
        ],
        "title": "PublicReplay",
        "description": "A shared flight for the 3D replay. No pilot identity, no flight id, no clock time,\nno maximum altitude field; masked stretches are gaps between parts."
      },
      "PublicReplayPart": {
        "properties": {
          "t": {
            "items": {
              "type": "number"
            },
            "type": "array",
            "title": "T",
            "description": "Seconds since the first returned fix (0.1 s)"
          },
          "lon": {
            "items": {
              "type": "number"
            },
            "type": "array",
            "title": "Lon"
          },
          "lat": {
            "items": {
              "type": "number"
            },
            "type": "array",
            "title": "Lat"
          },
          "alt_gps": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array",
            "title": "Alt Gps",
            "description": "GNSS altitude, m"
          },
          "alt_baro": {
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array",
            "title": "Alt Baro",
            "description": "Pressure altitude, m; null without sensor"
          },
          "vario": {
            "items": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array",
            "title": "Vario",
            "description": "Vertical speed, m/s; null when unknown"
          }
        },
        "type": "object",
        "required": [
          "t",
          "lon",
          "lat",
          "alt_gps",
          "alt_baro",
          "vario"
        ],
        "title": "PublicReplayPart",
        "description": "One unmasked stretch of the flight as parallel arrays (one entry per point)."
      },
      "PublicReplaySummary": {
        "properties": {
          "flown_on": {
            "type": "string",
            "format": "date",
            "title": "Flown On",
            "description": "Day of the flight (no time of day)"
          },
          "duration_s": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration S"
          },
          "distance_m": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Distance M"
          },
          "takeoff_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Takeoff Name",
            "description": "Nearest public (approved) take-off site within 500 m, if any"
          },
          "landing_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Landing Name",
            "description": "Nearest public (approved) landing site within 500 m"
          },
          "fix_count": {
            "type": "integer",
            "title": "Fix Count",
            "description": "Visible (unmasked, usable) fixes before thinning"
          },
          "point_count": {
            "type": "integer",
            "title": "Point Count",
            "description": "Points returned"
          }
        },
        "type": "object",
        "required": [
          "flown_on",
          "duration_s",
          "distance_m",
          "takeoff_name",
          "landing_name",
          "fix_count",
          "point_count"
        ],
        "title": "PublicReplaySummary"
      },
      "PublicReplayThermal": {
        "properties": {
          "lon": {
            "type": "number",
            "title": "Lon",
            "description": "Core of the thermal"
          },
          "lat": {
            "type": "number",
            "title": "Lat"
          },
          "start_s": {
            "type": "number",
            "title": "Start S",
            "description": "Start, seconds on the same clock as the parts"
          },
          "duration_s": {
            "type": "integer",
            "title": "Duration S"
          },
          "gain_m": {
            "type": "integer",
            "title": "Gain M",
            "description": "Height gained, m"
          },
          "avg_climb_ms": {
            "type": "number",
            "title": "Avg Climb Ms",
            "description": "Mean climb rate, m/s"
          }
        },
        "type": "object",
        "required": [
          "lon",
          "lat",
          "start_s",
          "duration_s",
          "gain_m",
          "avg_climb_ms"
        ],
        "title": "PublicReplayThermal"
      },
      "RegionCollection": {
        "properties": {
          "type": {
            "type": "string",
            "const": "FeatureCollection",
            "title": "Type"
          },
          "features": {
            "items": {
              "$ref": "#/components/schemas/RegionFeature"
            },
            "type": "array",
            "title": "Features"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "features"
        ],
        "title": "RegionCollection"
      },
      "RegionFeature": {
        "properties": {
          "type": {
            "type": "string",
            "const": "Feature",
            "title": "Type"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id"
          },
          "bbox": {
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ],
            "type": "array",
            "maxItems": 4,
            "minItems": 4,
            "title": "Bbox",
            "description": "[west, south, east, north] of the full boundary, degrees"
          },
          "geometry": {
            "$ref": "#/components/schemas/MultiPolygonGeometry"
          },
          "properties": {
            "$ref": "#/components/schemas/RegionListProperties"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "id",
          "bbox",
          "geometry",
          "properties"
        ],
        "title": "RegionFeature"
      },
      "RegionHourClimb": {
        "properties": {
          "hour_bucket": {
            "type": "integer",
            "maximum": 11,
            "minimum": 0,
            "title": "Hour Bucket",
            "description": "Local 2-hour bucket (Europe/Istanbul), 0 = 00:00-02:00"
          },
          "avg_climb_ms": {
            "type": "number",
            "title": "Avg Climb Ms",
            "description": "Mean climb of the thermals in the region, m/s"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "hour_bucket",
          "avg_climb_ms"
        ],
        "title": "RegionHourClimb"
      },
      "RegionIncidentsResponse": {
        "properties": {
          "region": {
            "$ref": "#/components/schemas/IncidentRegion"
          },
          "incidents": {
            "items": {
              "$ref": "#/components/schemas/PublicIncident"
            },
            "type": "array",
            "title": "Incidents",
            "description": "Approved incidents of the region, by period, newest first (no ranking)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "region",
          "incidents"
        ],
        "title": "RegionIncidentsResponse"
      },
      "RegionListProperties": {
        "properties": {
          "slug": {
            "type": "string",
            "title": "Slug",
            "description": "Short name (`regions.slug`), used in Atlas addresses"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "title": "Updated At",
            "description": "Last change of the region (UTC)"
          },
          "summary": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RegionSummary"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null when the profile is not computed yet or withheld after a threshold increase (as `GET /v1/regions/{region}/profile`)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "slug",
          "name",
          "updated_at",
          "summary"
        ],
        "title": "RegionListProperties"
      },
      "RegionMonth": {
        "properties": {
          "month": {
            "type": "integer",
            "maximum": 12,
            "minimum": 1,
            "title": "Month",
            "description": "Local calendar month"
          },
          "flyable_days": {
            "type": "integer",
            "minimum": 0,
            "title": "Flyable Days",
            "description": "Local dates in this month, over all years, on which at least one flight from the region lasted longer than 15 minutes"
          },
          "years": {
            "type": "integer",
            "minimum": 1,
            "title": "Years",
            "description": "Years with flights in this month (divide for a mean)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "month",
          "flyable_days",
          "years"
        ],
        "title": "RegionMonth"
      },
      "RegionProfile": {
        "properties": {
          "version": {
            "type": "integer",
            "const": 1,
            "title": "Version",
            "description": "Profile rules and shape version"
          },
          "processing_version": {
            "type": "integer",
            "minimum": 1,
            "title": "Processing Version",
            "description": "Fix pipeline version of the inputs"
          },
          "thresholds": {
            "$ref": "#/components/schemas/RegionThresholds"
          },
          "support": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RegionSupport"
              },
              {
                "type": "null"
              }
            ],
            "description": "Band of pilots behind the region's flights; null below the threshold"
          },
          "takeoffs": {
            "items": {
              "$ref": "#/components/schemas/RegionTakeoff"
            },
            "type": "array",
            "title": "Takeoffs",
            "description": "Approved take-offs in the region"
          },
          "months": {
            "items": {
              "$ref": "#/components/schemas/RegionMonth"
            },
            "type": "array",
            "title": "Months",
            "description": "Months passing the threshold, in order"
          },
          "wind": {
            "$ref": "#/components/schemas/RegionWind"
          },
          "avg_flight_duration_min": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Avg Flight Duration Min",
            "description": "Mean airborne time of the region's flights, minutes"
          },
          "hours": {
            "items": {
              "$ref": "#/components/schemas/RegionHourClimb"
            },
            "type": "array",
            "title": "Hours",
            "description": "Hour buckets passing the threshold, in order"
          },
          "best_hour_bucket": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 11,
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Best Hour Bucket",
            "description": "Hour bucket with the highest mean climb"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "version",
          "processing_version",
          "thresholds",
          "support",
          "takeoffs",
          "months",
          "wind",
          "avg_flight_duration_min",
          "hours",
          "best_hour_bucket"
        ],
        "title": "RegionProfile",
        "description": "`regions.profile` (docs/02 regions); values below the thresholds are absent."
      },
      "RegionProfileResponse": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id"
          },
          "slug": {
            "type": "string",
            "title": "Slug"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "title": "Updated At",
            "description": "Last change of the region (UTC)"
          },
          "profile": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RegionProfile"
              },
              {
                "type": "null"
              }
            ],
            "description": "Null when not computed yet, or withheld after a threshold increase until the next nightly run"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "updated_at",
          "profile"
        ],
        "title": "RegionProfileResponse"
      },
      "RegionSummary": {
        "properties": {
          "support": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/RegionSupport"
              },
              {
                "type": "null"
              }
            ],
            "description": "Band of pilots behind the region's flights; null below the threshold"
          },
          "months": {
            "items": {
              "type": "integer"
            },
            "type": "array",
            "title": "Months",
            "description": "Months (1-12) passing the threshold, in order; a missing month means 'not enough data', not 'not flown'"
          },
          "dominant_sector": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 7,
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Dominant Sector",
            "description": "Dominant 850 hPa wind sector (0 = north, 45° steps)"
          },
          "best_hour_bucket": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 11,
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Best Hour Bucket",
            "description": "Local 2-hour bucket with the highest mean climb"
          },
          "avg_flight_duration_min": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Avg Flight Duration Min",
            "description": "Mean airborne time of the region's flights, minutes"
          },
          "takeoffs": {
            "type": "integer",
            "minimum": 0,
            "title": "Takeoffs",
            "description": "Approved take-offs in the region (profile list)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "support",
          "months",
          "dominant_sector",
          "best_hour_bucket",
          "avg_flight_duration_min",
          "takeoffs"
        ],
        "title": "RegionSummary",
        "description": "Headline values of the region profile; every one is absent below the thresholds."
      },
      "RegionSupport": {
        "properties": {
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "Confidence",
            "description": "Pilots behind the region's flights: `low` (3 to 4), `medium` (5 to 9), `high` (10 or more)"
          },
          "min_pilots": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Pilots",
            "description": "At least this many different pilots, floored to 3, 5, 10, 20, 50, 100; the exact count is never returned"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "confidence",
          "min_pilots"
        ],
        "title": "RegionSupport"
      },
      "RegionTakeoff": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id",
            "description": "Site id (`GET /v1/sites`)"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Name given by a moderator; null if none yet"
          },
          "lon": {
            "type": "number",
            "maximum": 180,
            "minimum": -180,
            "title": "Lon",
            "description": "Longitude, WGS 84, 6 decimals"
          },
          "lat": {
            "type": "number",
            "maximum": 90,
            "minimum": -90,
            "title": "Lat",
            "description": "Latitude, WGS 84, 6 decimals"
          },
          "headings": {
            "anyOf": [
              {
                "items": {
                  "type": "integer"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Headings",
            "description": "Launch directions (degrees, 45° sectors)"
          },
          "elevation_m": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Elevation M",
            "description": "Ground elevation in metres (DEM)"
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "Confidence"
          },
          "min_pilots": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Pilots"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "name",
          "lon",
          "lat",
          "headings",
          "elevation_m",
          "confidence",
          "min_pilots"
        ],
        "title": "RegionTakeoff"
      },
      "RegionThresholds": {
        "properties": {
          "min_pilots": {
            "type": "integer",
            "minimum": 1,
            "title": "Min Pilots",
            "description": "`min_pilots_regions` used for this profile"
          },
          "min_flights": {
            "type": "integer",
            "minimum": 1,
            "title": "Min Flights",
            "description": "`min_flights_regions` used for this profile"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "min_pilots",
          "min_flights"
        ],
        "title": "RegionThresholds"
      },
      "RegionWind": {
        "properties": {
          "dominant_sector": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 7,
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Dominant Sector",
            "description": "Sector with the most flights when its share is at least 0.3"
          },
          "sectors": {
            "items": {
              "$ref": "#/components/schemas/RegionWindSector"
            },
            "type": "array",
            "title": "Sectors"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "dominant_sector",
          "sectors"
        ],
        "title": "RegionWind"
      },
      "RegionWindSector": {
        "properties": {
          "sector": {
            "type": "integer",
            "maximum": 7,
            "minimum": 0,
            "title": "Sector",
            "description": "850 hPa wind the air came from, 45° sectors, 0 = north"
          },
          "share": {
            "type": "number",
            "maximum": 1,
            "minimum": 0,
            "title": "Share",
            "description": "Share of the flights with a known sector"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "sector",
          "share"
        ],
        "title": "RegionWindSector"
      },
      "RouteGraphInfo": {
        "properties": {
          "built_at": {
            "type": "string",
            "format": "date-time",
            "title": "Built At",
            "description": "Last change of the region's precomputed graph"
          },
          "rule_version": {
            "type": "integer",
            "title": "Rule Version"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "built_at",
          "rule_version"
        ],
        "title": "RouteGraphInfo"
      },
      "RouteLanding": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id",
            "description": "Public landing (`GET /v1/sites`)"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "lat": {
            "type": "number",
            "title": "Lat"
          },
          "lon": {
            "type": "number",
            "title": "Lon"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "name",
          "lat",
          "lon"
        ],
        "title": "RouteLanding"
      },
      "RouteOption": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id",
            "description": "Stable within the answer: the direction code"
          },
          "direction": {
            "type": "string",
            "enum": [
              "n",
              "ne",
              "e",
              "se",
              "s",
              "sw",
              "w",
              "nw"
            ],
            "title": "Direction",
            "description": "General direction from the take-off"
          },
          "waypoints": {
            "items": {
              "$ref": "#/components/schemas/RouteWaypoint"
            },
            "type": "array",
            "title": "Waypoints"
          },
          "reasons": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Reasons",
            "description": "i18n keys `routes.reason.<code>` (docs/10 §20)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "direction",
          "waypoints",
          "reasons"
        ],
        "title": "RouteOption"
      },
      "RoutesResponse": {
        "properties": {
          "takeoff": {
            "$ref": "#/components/schemas/RouteTakeoff"
          },
          "date": {
            "type": "string",
            "format": "date",
            "title": "Date",
            "description": "Local date (Europe/Istanbul)"
          },
          "hour": {
            "type": "integer",
            "maximum": 23,
            "minimum": 0,
            "title": "Hour",
            "description": "Local hour (Europe/Istanbul)"
          },
          "timezone": {
            "type": "string",
            "const": "Europe/Istanbul",
            "title": "Timezone"
          },
          "wind": {
            "$ref": "#/components/schemas/RouteWind"
          },
          "options": {
            "items": {
              "$ref": "#/components/schemas/RouteOption"
            },
            "type": "array",
            "title": "Options",
            "description": "A few options in compass order (clockwise from north); not ranked"
          },
          "empty": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Empty",
            "description": "i18n key `routes.empty.<code>` when there is no option; else null"
          },
          "forecast": {
            "$ref": "#/components/schemas/ForecastInfo"
          },
          "graph": {
            "$ref": "#/components/schemas/RouteGraphInfo"
          },
          "notice": {
            "type": "string",
            "const": "routes.notice.preflight_only",
            "title": "Notice",
            "description": "i18n key of the fixed notice (docs/04 §3)"
          },
          "notice_text": {
            "$ref": "#/components/schemas/NoticeText",
            "description": "The notice itself: pre-flight planning only; the pilot decides; airspace and legal limits are checked separately"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "takeoff",
          "date",
          "hour",
          "timezone",
          "wind",
          "options",
          "empty",
          "forecast",
          "graph",
          "notice",
          "notice_text"
        ],
        "title": "RoutesResponse"
      },
      "RouteTakeoff": {
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "lat": {
            "type": "number",
            "title": "Lat"
          },
          "lon": {
            "type": "number",
            "title": "Lon"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "name",
          "lat",
          "lon"
        ],
        "title": "RouteTakeoff"
      },
      "RouteWaypoint": {
        "properties": {
          "lat": {
            "type": "number",
            "title": "Lat",
            "description": "Centre of the thermal cell (H3 r9, about 0.1 km²)"
          },
          "lon": {
            "type": "number",
            "title": "Lon"
          },
          "support": {
            "type": "string",
            "enum": [
              "high",
              "medium"
            ],
            "title": "Support",
            "description": "Support band of the cell over all months (`high` from 10 pilots, `medium` from 5; never an exact count)"
          },
          "conditions": {
            "type": "string",
            "enum": [
              "hour_and_wind",
              "wind",
              "hour",
              "general"
            ],
            "title": "Conditions",
            "description": "Which breakdown weighted the cell: the day's hour and wind sector, the sector only, the hour only, or the general pattern"
          },
          "along_skyway": {
            "type": "boolean",
            "title": "Along Skyway",
            "description": "The leg arriving here follows a public skyway"
          },
          "landings": {
            "items": {
              "$ref": "#/components/schemas/RouteLanding"
            },
            "type": "array",
            "title": "Landings",
            "description": "Landing alternatives within reach of this waypoint, nearest first"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "lat",
          "lon",
          "support",
          "conditions",
          "along_skyway",
          "landings"
        ],
        "title": "RouteWaypoint"
      },
      "RouteWind": {
        "properties": {
          "sector": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 7,
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Sector",
            "description": "850 hPa wind sector used for weighting (0 = from the north, 45° slices clockwise); null without a forecast"
          },
          "direction_deg": {
            "anyOf": [
              {
                "type": "integer",
                "maximum": 360,
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Direction Deg",
            "description": "850 hPa wind, from"
          },
          "speed_ms": {
            "anyOf": [
              {
                "type": "number",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Speed Ms",
            "description": "850 hPa wind speed, m/s"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "sector",
          "direction_deg",
          "speed_ms"
        ],
        "title": "RouteWind"
      },
      "ShareOut": {
        "properties": {
          "public_id": {
            "type": "string",
            "title": "Public Id",
            "description": "Unguessable link id: 22 characters of `0-9A-Za-z` (about 131 random bits), not derived from the flight"
          },
          "url_path": {
            "type": "string",
            "title": "Url Path",
            "description": "Path of the Atlas page, `/flights/{public_id}`; the client joins it to the Atlas origin"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "When this share was opened (UTC)"
          }
        },
        "type": "object",
        "required": [
          "public_id",
          "url_path",
          "created_at"
        ],
        "title": "ShareOut",
        "description": "The flight's active share. No pilot identity, no flight data."
      },
      "ShareRequestBody": {
        "properties": {
          "external_pilot_hash": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{64}$",
            "title": "External Pilot Hash",
            "description": "HMAC-SHA256 of the client's pilot id, 64 hex characters; the same value as `external_pilot_hash` of the upload. The flight must be this pilot's"
          },
          "consent_version": {
            "type": "integer",
            "maximum": 32767,
            "minimum": 0,
            "title": "Consent Version",
            "description": "Version of the consent text the pilot shared (or stopped sharing) under; stored with the `public_replay` consent event"
          },
          "source": {
            "type": "string",
            "const": "kunduzo",
            "title": "Source",
            "description": "Platform the pilot acted on"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "external_pilot_hash",
          "consent_version",
          "source"
        ],
        "title": "ShareRequestBody",
        "description": "The pilot who acts and the consent text version they acted on."
      },
      "Site": {
        "properties": {
          "type": {
            "type": "string",
            "const": "Feature",
            "title": "Type"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "title": "Id"
          },
          "geometry": {
            "$ref": "#/components/schemas/PointGeometry"
          },
          "properties": {
            "$ref": "#/components/schemas/SiteProperties"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "id",
          "geometry",
          "properties"
        ],
        "title": "Site"
      },
      "SiteCollection": {
        "properties": {
          "type": {
            "type": "string",
            "const": "FeatureCollection",
            "title": "Type"
          },
          "features": {
            "items": {
              "$ref": "#/components/schemas/Site"
            },
            "type": "array",
            "title": "Features"
          },
          "next_cursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Cursor",
            "description": "Pass as `cursor` for the next page; null on the last page"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "type",
          "features",
          "next_cursor"
        ],
        "title": "SiteCollection"
      },
      "SiteProperties": {
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "takeoff",
              "landing"
            ],
            "title": "Kind"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Name given by a moderator; null if none yet"
          },
          "headings": {
            "anyOf": [
              {
                "items": {
                  "type": "integer"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Headings",
            "description": "Takeoff: launch directions (downhill azimuth, degrees, 45° sectors); null when not derived (no DEM yet) or for landings"
          },
          "slope_deg": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Slope Deg",
            "description": "Takeoff: slope in degrees (DEM)"
          },
          "elevation_m": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Elevation M",
            "description": "Takeoff: ground elevation in metres (DEM)"
          },
          "area_m2": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Area M2",
            "description": "Landing: area of the landing points' hull, m²"
          },
          "confidence": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "title": "Confidence",
            "description": "Support from flight data: `low` (3 to 4 pilots), `medium` (5 to 9), `high` (10 or more). Moderator or OSM sites below 3 pilots are `low`"
          },
          "min_pilots": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Pilots",
            "description": "At least this many different pilots used the site, floored to 3, 5, 10, 20, 50, 100; null for moderator or OSM sites under 3. The exact count is never returned"
          },
          "version": {
            "type": "integer",
            "title": "Version",
            "description": "Increments on every change of the site"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "title": "Updated At",
            "description": "Last change (UTC)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "kind",
          "name",
          "headings",
          "slope_deg",
          "elevation_m",
          "area_m2",
          "confidence",
          "min_pilots",
          "version",
          "updated_at"
        ],
        "title": "SiteProperties",
        "description": "Public attributes of a site. Never: pilot identity, flights, exact pilot count."
      },
      "SlopeConditions": {
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "title": "Date",
            "description": "Local date (Europe/Istanbul)"
          },
          "timezone": {
            "type": "string",
            "const": "Europe/Istanbul",
            "title": "Timezone"
          },
          "cell": {
            "$ref": "#/components/schemas/ForecastCell",
            "description": "Centre of the 0.25° cell the answer is for"
          },
          "hours": {
            "items": {
              "$ref": "#/components/schemas/SlopeHour"
            },
            "type": "array",
            "title": "Hours",
            "description": "Local hours 05-21"
          },
          "forecast_state": {
            "type": "string",
            "enum": [
              "available",
              "out_of_range",
              "unavailable"
            ],
            "title": "Forecast State",
            "description": "`available`; `out_of_range` (date outside today..today + 4, sun only); `unavailable` (forecast server off or failed, sun only)"
          },
          "forecast": {
            "$ref": "#/components/schemas/ForecastInfo"
          },
          "method_version": {
            "type": "integer",
            "title": "Method Version",
            "description": "Version of the slope rule (docs/07 §9b)"
          },
          "notice": {
            "type": "string",
            "const": "slopes.notice.preflight_only",
            "title": "Notice",
            "description": "i18n key of the fixed notice: pre-flight planning only; the pilot decides"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "date",
          "timezone",
          "cell",
          "hours",
          "forecast_state",
          "forecast",
          "method_version",
          "notice"
        ],
        "title": "SlopeConditions"
      },
      "SlopeHour": {
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "title": "Time",
            "description": "UTC hour; values are instantaneous"
          },
          "local_hour": {
            "type": "integer",
            "maximum": 23,
            "minimum": 0,
            "title": "Local Hour",
            "description": "Hour in Europe/Istanbul"
          },
          "sun": {
            "$ref": "#/components/schemas/Sun",
            "description": "Sun at the cell centre (style state sun_azimuth/altitude)"
          },
          "wind_10m": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Wind"
              },
              {
                "type": "null"
              }
            ],
            "description": "10 m forecast wind; null without a forecast"
          },
          "wind_850hpa": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Wind"
              },
              {
                "type": "null"
              }
            ],
            "description": "850 hPa forecast wind (about 1,500 m); null without a forecast"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "time",
          "local_hour",
          "sun",
          "wind_10m",
          "wind_850hpa"
        ],
        "title": "SlopeHour"
      },
      "Sport": {
        "type": "string",
        "enum": [
          "paragliding",
          "hang_gliding",
          "sailplane",
          "paramotor",
          "hike_and_fly",
          "other"
        ],
        "title": "Sport",
        "description": "`flights.sport` (migration 0016, SPORT-001). Layers count only `PARAGLIDING` for now."
      },
      "SuitabilityHour": {
        "properties": {
          "time": {
            "type": "string",
            "format": "date-time",
            "title": "Time",
            "description": "UTC hour; forecast values are instantaneous"
          },
          "level": {
            "type": "string",
            "enum": [
              "green",
              "yellow",
              "red",
              "unknown"
            ],
            "title": "Level"
          },
          "reasons": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Reasons",
            "description": "i18n keys `suitability.reason.<code>`"
          },
          "wind_10m": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Wind"
              },
              {
                "type": "null"
              }
            ],
            "description": "10 m wind; null without a forecast"
          },
          "wind_850hpa": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Wind"
              },
              {
                "type": "null"
              }
            ],
            "description": "850 hPa wind (about 1,500 m)"
          },
          "heading_offset_deg": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Heading Offset Deg",
            "description": "Angle of the 10 m wind to the nearest launch heading (0 = straight in); null for light wind, unknown headings or no forecast"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "time",
          "level",
          "reasons",
          "wind_10m",
          "wind_850hpa",
          "heading_offset_deg"
        ],
        "title": "SuitabilityHour"
      },
      "SuitabilityResponse": {
        "properties": {
          "site_id": {
            "type": "string",
            "format": "uuid",
            "title": "Site Id"
          },
          "date": {
            "type": "string",
            "format": "date",
            "title": "Date",
            "description": "Local date (Europe/Istanbul)"
          },
          "timezone": {
            "type": "string",
            "const": "Europe/Istanbul",
            "title": "Timezone"
          },
          "headings": {
            "anyOf": [
              {
                "items": {
                  "type": "integer"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Headings",
            "description": "Launch directions used (downhill azimuth, degrees); null when not derived"
          },
          "slots": {
            "items": {
              "$ref": "#/components/schemas/SuitabilitySlot"
            },
            "type": "array",
            "title": "Slots"
          },
          "forecast": {
            "$ref": "#/components/schemas/ForecastInfo"
          },
          "rule_version": {
            "type": "integer",
            "title": "Rule Version",
            "description": "Version of the rating rule"
          },
          "notice": {
            "type": "string",
            "const": "suitability.notice.preflight_only",
            "title": "Notice",
            "description": "i18n key of the fixed notice: pre-flight planning only, not for in-flight use; the pilot decides (docs/04 §3)"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "site_id",
          "date",
          "timezone",
          "headings",
          "slots",
          "forecast",
          "rule_version",
          "notice"
        ],
        "title": "SuitabilityResponse"
      },
      "SuitabilitySlot": {
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time",
            "title": "Start",
            "description": "Local start (Europe/Istanbul, with offset)"
          },
          "end": {
            "type": "string",
            "format": "date-time",
            "title": "End",
            "description": "Local end (exclusive)"
          },
          "level": {
            "type": "string",
            "enum": [
              "green",
              "yellow",
              "red",
              "unknown"
            ],
            "title": "Level",
            "description": "`green` looks suitable, `yellow` caution, `red` looks unsuitable, `unknown` no forecast; the worst of the slot's hours"
          },
          "reasons": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Reasons",
            "description": "i18n keys `suitability.reason.<code>` (docs/10 §14)"
          },
          "hours": {
            "items": {
              "$ref": "#/components/schemas/SuitabilityHour"
            },
            "type": "array",
            "title": "Hours"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "start",
          "end",
          "level",
          "reasons",
          "hours"
        ],
        "title": "SuitabilitySlot"
      },
      "Sun": {
        "properties": {
          "azimuth_deg": {
            "type": "number",
            "exclusiveMaximum": 360,
            "minimum": 0,
            "title": "Azimuth Deg",
            "description": "Clockwise from north, one decimal"
          },
          "altitude_deg": {
            "type": "number",
            "maximum": 90,
            "minimum": -90,
            "title": "Altitude Deg",
            "description": "Above the horizon, one decimal; negative = night"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "azimuth_deg",
          "altitude_deg"
        ],
        "title": "Sun"
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "VideoFormat": {
        "type": "string",
        "enum": [
          "16x9",
          "9x16"
        ],
        "title": "VideoFormat",
        "description": "Aspect ratio in the URL and the object key; both are 1080p."
      },
      "VideoInfo": {
        "properties": {
          "public_id": {
            "type": "string",
            "title": "Public Id"
          },
          "format": {
            "type": "string",
            "enum": [
              "16:9",
              "9:16"
            ],
            "title": "Format"
          },
          "length_s": {
            "type": "integer",
            "enum": [
              15,
              30
            ],
            "title": "Length S",
            "description": "Longest video, seconds: `15` (default; about 3.5 minutes to make) or `30` (about 7 minutes). A flight is shown at least 10x faster, so a short flight gives a shorter video"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "rendering",
              "ready",
              "failed"
            ],
            "title": "Status",
            "description": "`queued` → `rendering` → `ready`; `failed` after every retry failed"
          },
          "status_path": {
            "type": "string",
            "title": "Status Path",
            "description": "Path of the status route of this video"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Url",
            "description": "Time-limited download URL (MP4, H.264) when `ready`, else null"
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At",
            "description": "`url` stops working at this time (UTC)"
          },
          "width": {
            "type": "integer",
            "title": "Width"
          },
          "height": {
            "type": "integer",
            "title": "Height"
          },
          "fps": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Fps",
            "description": "30 when ready"
          },
          "duration_s": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Duration S",
            "description": "Length of the video, seconds, when ready"
          },
          "size_bytes": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Size Bytes"
          },
          "sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sha256",
            "description": "SHA-256 of the MP4 when ready"
          },
          "error": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "replay_gone",
                  "render_failed"
                ]
              },
              {
                "type": "null"
              }
            ],
            "title": "Error",
            "description": "Why it failed: `render_failed` (asking again retries), `replay_gone`"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "public_id",
          "format",
          "length_s",
          "status",
          "status_path",
          "url",
          "expires_at",
          "width",
          "height",
          "fps",
          "duration_s",
          "size_bytes",
          "sha256",
          "error"
        ],
        "title": "VideoInfo",
        "description": "A video of a shared flight. No pilot, no flight id: the share's `public_id` only."
      },
      "VideoLength": {
        "type": "integer",
        "enum": [
          15,
          30
        ],
        "title": "VideoLength",
        "description": "Longest video, seconds: what a request asks for (Q-023). The worker's\n`UPDRAFT_RENDER_MAX_SECONDS` (default 30) caps it."
      },
      "VideoRequest": {
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "16:9",
              "9:16"
            ],
            "title": "Format",
            "description": "Aspect ratio: `16:9` landscape or `9:16` portrait (stories, reels)"
          },
          "resolution": {
            "type": "string",
            "const": "1080p",
            "title": "Resolution",
            "description": "Only 1080p (1920x1080 / 1080x1920)",
            "default": "1080p"
          },
          "length_s": {
            "type": "integer",
            "enum": [
              15,
              30
            ],
            "title": "Length S",
            "description": "Longest video, seconds: `15` (default; about 3.5 minutes to make) or `30` (about 7 minutes). A flight is shown at least 10x faster, so a short flight gives a shorter video",
            "default": 15
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "format"
        ],
        "title": "VideoRequest"
      },
      "Wind": {
        "properties": {
          "direction_deg": {
            "type": "integer",
            "maximum": 360,
            "minimum": 0,
            "title": "Direction Deg",
            "description": "Where the wind comes from, degrees"
          },
          "speed_ms": {
            "type": "number",
            "minimum": 0,
            "title": "Speed Ms",
            "description": "Speed in m/s, one decimal"
          }
        },
        "additionalProperties": false,
        "type": "object",
        "required": [
          "direction_deg",
          "speed_ms"
        ],
        "title": "Wind"
      }
    },
    "securitySchemes": {
      "APIKeyHeader": {
        "type": "apiKey",
        "description": "API key issued by Updraft (`updraft keys create`).",
        "in": "header",
        "name": "X-Api-Key"
      }
    }
  }
}
