{
  "openapi": "3.1.0",
  "info": {
    "title": "Окно — Moscow apartment environment API",
    "version": "0.4.0",
    "description": "Сценарии 3D за 2026 год и подготовленные отчёты 7 объявлений плюс адресного кейса по ERA5 2023–2025. История шума, самолётов, поездов и пробок не подключена; геометрия текущая. Модель 0.4.0."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "paths": {
    "/api/v1/listing": {
      "post": {
        "operationId": "importListing",
        "description": "Пробный импорт публичной страницы по HTTPS allowlist. CAPTCHA не обходится: status blocked и ручной текст. Адрес, этаж, план и ориентацию проверяет пользователь.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "text": {
                    "type": "string",
                    "maxLength": 40000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingDraft"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Источник данных недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "health",
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Источник данных недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "operationId": "searchAddress",
        "description": "Ручной поиск, без автодополнения. buildingId может быть родителем частей; API разрешает его в конкретную часть.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Московский адрес",
            "schema": {
              "type": "string",
              "minLength": 3,
              "maxLength": 180
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Источник данных недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/scene": {
      "get": {
        "operationId": "getScene",
        "description": "id готовой сцены либо lat и lon. x восток, y север, метры от scene.center.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Готовый снимок",
            "schema": {
              "enum": [
                "belorussky_station",
                "frunzenskaya",
                "ramenki"
              ]
            }
          },
          {
            "name": "lat",
            "in": "query",
            "required": false,
            "description": "Широта",
            "schema": {
              "type": "number",
              "minimum": 55.45,
              "maximum": 56.05
            }
          },
          {
            "name": "lon",
            "in": "query",
            "required": false,
            "description": "Долгота",
            "schema": {
              "type": "number",
              "minimum": 37.15,
              "maximum": 38.15
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Scene"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Источник данных недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/analyze": {
      "post": {
        "operationId": "analyzeApartment",
        "description": "config дополняется DEFAULT_CONFIG. buildingId обязателен. Координаты квартиры в ответе относятся к выбранному объёму. Для собственных данных вызовите makeReport(Scene, config) в своём адаптере.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "config"
                ],
                "properties": {
                  "sceneId": {
                    "type": "string"
                  },
                  "lat": {
                    "type": "number"
                  },
                  "lon": {
                    "type": "number"
                  },
                  "config": {
                    "$ref": "#/components/schemas/Config"
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "sceneId"
                    ]
                  },
                  {
                    "required": [
                      "lat",
                      "lon"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Успешный ответ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Report"
                }
              }
            }
          },
          "400": {
            "description": "Некорректные параметры",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Источник данных недоступен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/portfolio": {
      "get": {
        "operationId": "listPortfolio",
        "description": "Семь снимков объявлений и адресный кейс. У адресного кейса kind=address-case; неизвестные параметры квартиры равны null, допущения отдельно в scenarioDefaults.",
        "responses": {
          "200": {
            "description": "schemaVersion и listings"
          }
        }
      },
      "post": {
        "operationId": "analyzePortfolio",
        "description": "Собрать отчёт по сохранённым погодным и геометрическим данным. Матрица лучей/ветра подготовлена заранее; временной период выбирается при запросе. Фасады — альтернативные гипотезы, не достоверное положение квартиры.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "listingId",
                  "years"
                ],
                "additionalProperties": false,
                "properties": {
                  "listingId": {
                    "type": "string",
                    "enum": [
                      "marushkino",
                      "pravdy",
                      "frunzenskaya-flat",
                      "yamskogo",
                      "mnevniki",
                      "medikov",
                      "sovkhoznaya",
                      "molzhaninovo"
                    ]
                  },
                  "years": {
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "schemaVersion 2.0, period, case (facades, geometry, neighborhood, construction), coverage"
          },
          "400": {
            "description": "Некорректный ID, период или поля"
          },
          "413": {
            "description": "Тело больше 2048 байт"
          }
        }
      }
    },
    "/api/v1/sources": {
      "get": {
        "operationId": "getSources",
        "description": "Каталог источников, статусов подключения и условий. Включает подробные первичные доказательства для погоды, окружения и площадок.",
        "responses": {
          "200": {
            "description": "sources и evidence"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Config": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "buildingId"
        ],
        "properties": {
          "buildingId": {
            "type": "string",
            "default": ""
          },
          "floor": {
            "type": "number",
            "minimum": 1,
            "maximum": 100,
            "default": 5
          },
          "facade": {
            "type": "number",
            "minimum": 0,
            "maximum": 10000,
            "default": 0
          },
          "secondFacade": {
            "type": "number",
            "minimum": -1,
            "maximum": 10000,
            "default": -1
          },
          "windowPosition": {
            "type": "number",
            "minimum": 0.05,
            "maximum": 0.95,
            "default": 0.5
          },
          "month": {
            "type": "number",
            "minimum": 1,
            "maximum": 12,
            "default": 6
          },
          "day": {
            "type": "number",
            "minimum": 1,
            "maximum": 31,
            "default": 21
          },
          "hour": {
            "type": "number",
            "minimum": 0,
            "maximum": 23.983333333333334,
            "default": 14
          },
          "mode": {
            "type": "string",
            "enum": [
              "sun",
              "noise",
              "wind"
            ],
            "default": "sun"
          },
          "windDirection": {
            "type": "number",
            "minimum": 0,
            "maximum": 360,
            "default": 270
          },
          "windSpeed": {
            "type": "number",
            "minimum": 0,
            "maximum": 15,
            "default": 4
          },
          "ventilation": {
            "type": "string",
            "enum": [
              "cross",
              "corner",
              "single"
            ],
            "default": "cross"
          },
          "windowArea": {
            "type": "number",
            "minimum": 0,
            "maximum": 3,
            "default": 0.6
          },
          "roomArea": {
            "type": "number",
            "minimum": 10,
            "maximum": 250,
            "default": 55
          },
          "ceilingHeight": {
            "type": "number",
            "minimum": 2,
            "maximum": 5,
            "default": 2.7
          },
          "doorOpen": {
            "type": "boolean",
            "default": true
          },
          "roomDepth": {
            "type": "number",
            "minimum": 2,
            "maximum": 15,
            "default": 5
          },
          "windowHeight": {
            "type": "number",
            "minimum": 0.3,
            "maximum": 3,
            "default": 1.5
          },
          "sillHeight": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 0.8
          },
          "balconyDepth": {
            "type": "number",
            "minimum": 0,
            "maximum": 3,
            "default": 0
          },
          "traffic": {
            "type": "number",
            "minimum": 0,
            "maximum": 3,
            "default": 1
          },
          "trainsPerHour": {
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 6
          },
          "railEnabled": {
            "type": "boolean",
            "default": true
          },
          "roadEnabled": {
            "type": "boolean",
            "default": true
          },
          "closedWindows": {
            "type": "boolean",
            "default": false
          },
          "soundReduction": {
            "type": "number",
            "minimum": 0,
            "maximum": 45,
            "default": 28
          },
          "aircraftEnabled": {
            "type": "boolean",
            "default": false
          },
          "runwayId": {
            "type": "string",
            "default": "auto",
            "enum": [
              "auto",
              "SVO-238543-06R",
              "SVO-238543-24L",
              "SVO-238544-06C",
              "SVO-238544-24C",
              "SVO-332323-06L",
              "SVO-332323-24R",
              "DME-238569-14C",
              "DME-238569-32C",
              "DME-238570-14R",
              "DME-238570-32L",
              "VKO-238555-01",
              "VKO-238555-19",
              "VKO-238556-06",
              "VKO-238556-24",
              "ZIA-osm-0",
              "ZIA-osm-1"
            ]
          },
          "aircraftOperation": {
            "type": "string",
            "default": "approach",
            "enum": [
              "approach",
              "departure"
            ]
          },
          "flightsPerHour": {
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 6
          },
          "aircraftSel": {
            "type": "number",
            "minimum": 65,
            "maximum": 115,
            "default": 90
          },
          "yardEnabled": {
            "type": "boolean",
            "default": false
          },
          "yardSource": {
            "type": "string",
            "default": "nearest",
            "enum": [
              "nearest",
              "custom"
            ]
          },
          "yardX": {
            "type": "number",
            "minimum": -2000,
            "maximum": 2000,
            "default": 0
          },
          "yardY": {
            "type": "number",
            "minimum": -2000,
            "maximum": 2000,
            "default": 0
          },
          "yardPeople": {
            "type": "number",
            "minimum": 0,
            "maximum": 40,
            "default": 5
          },
          "yardActiveMinutes": {
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 30
          },
          "yardVoiceLevel": {
            "type": "number",
            "minimum": 45,
            "maximum": 90,
            "default": 70
          },
          "yardReflection": {
            "type": "number",
            "minimum": 0,
            "maximum": 0.6,
            "default": 0.35
          },
          "constructionEnabled": {
            "type": "boolean",
            "default": false
          },
          "constructionSource": {
            "type": "string",
            "default": "nearest",
            "enum": [
              "nearest",
              "custom"
            ]
          },
          "constructionX": {
            "type": "number",
            "minimum": -2000,
            "maximum": 2000,
            "default": 0
          },
          "constructionY": {
            "type": "number",
            "minimum": -2000,
            "maximum": 2000,
            "default": 0
          },
          "constructionLevel": {
            "type": "number",
            "minimum": 50,
            "maximum": 110,
            "default": 85
          },
          "constructionActiveMinutes": {
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 30
          },
          "constructionStart": {
            "type": "string",
            "default": "",
            "pattern": "^(|20[2-4][0-9]-[0-9]{2}-[0-9]{2})$",
            "description": "YYYY-MM-DD или пустая строка, даты работ заданы пользователем и не выключают отдельный сценарий шумного часа."
          },
          "constructionEnd": {
            "type": "string",
            "default": "",
            "pattern": "^(|20[2-4][0-9]-[0-9]{2}-[0-9]{2})$",
            "description": "YYYY-MM-DD или пустая строка, даты работ заданы пользователем и не выключают отдельный сценарий шумного часа."
          },
          "occupants": {
            "type": "number",
            "minimum": 1,
            "maximum": 12,
            "default": 2
          },
          "apartmentArea": {
            "type": "number",
            "minimum": 10,
            "maximum": 1500,
            "default": 55
          },
          "livingArea": {
            "type": "number",
            "minimum": 5,
            "maximum": 250,
            "default": 30
          },
          "ventilationDuty": {
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 60
          }
        },
        "description": "facade: сначала рёбра внешнего кольца, затем дворовые. secondFacade -1 = авто. windDirection: откуда дует, градусы от севера. roomArea: проветриваемая площадь; single — одна комната. windowArea: свободное сечение каждого окна. traffic: сценарный множитель. trainsPerHour: частота на локальный пучок параллельных путей; веса развилок неизвестны."
      },
      "ListingDraft": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "provider": {
            "enum": [
              "cian",
              "avito",
              "yandex",
              "wildberries"
            ]
          },
          "status": {
            "enum": [
              "parsed",
              "partial",
              "text",
              "blocked"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "floor": {
            "type": [
              "number",
              "null"
            ]
          },
          "buildingFloors": {
            "type": [
              "number",
              "null"
            ]
          },
          "area": {
            "type": [
              "number",
              "null"
            ]
          },
          "rooms": {
            "type": [
              "number",
              "null"
            ]
          },
          "lat": {
            "type": [
              "number",
              "null"
            ]
          },
          "lon": {
            "type": [
              "number",
              "null"
            ]
          },
          "layout": {
            "enum": [
              "cross",
              "corner",
              "single",
              null
            ]
          },
          "windowFacing": {
            "enum": [
              "courtyard",
              "street",
              "both",
              null
            ]
          },
          "images": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "isPlan": {
                  "type": "boolean"
                }
              }
            }
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "Scene": {
        "type": "object",
        "required": [
          "id",
          "center",
          "buildings",
          "transport",
          "source"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "center": {
            "type": "object",
            "properties": {
              "lat": {
                "type": "number"
              },
              "lon": {
                "type": "number"
              }
            }
          },
          "buildings": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "polygon",
                "height",
                "levels"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "parentId": {
                  "type": "string"
                },
                "address": {
                  "type": "string"
                },
                "polygon": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "x",
                      "y"
                    ],
                    "properties": {
                      "x": {
                        "type": "number"
                      },
                      "y": {
                        "type": "number"
                      }
                    }
                  },
                  "minItems": 3
                },
                "holes": {
                  "type": "array",
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "x",
                        "y"
                      ],
                      "properties": {
                        "x": {
                          "type": "number"
                        },
                        "y": {
                          "type": "number"
                        }
                      }
                    },
                    "minItems": 3
                  }
                },
                "height": {
                  "type": "number"
                },
                "levels": {
                  "type": "number"
                },
                "minHeight": {
                  "type": "number"
                },
                "heightSource": {
                  "enum": [
                    "osm-height",
                    "osm-levels",
                    "assumed"
                  ]
                }
              }
            }
          },
          "transport": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "places": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "kind": {
                  "enum": [
                    "playground",
                    "construction"
                  ]
                },
                "point": {
                  "type": "object",
                  "required": [
                    "x",
                    "y"
                  ],
                  "properties": {
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    }
                  }
                },
                "sourceUrl": {
                  "type": "string"
                },
                "startDate": {
                  "type": "string"
                },
                "endDate": {
                  "type": "string"
                },
                "plannedOpening": {
                  "type": "string"
                }
              }
            }
          },
          "source": {
            "type": "object"
          }
        }
      },
      "Report": {
        "type": "object",
        "required": [
          "schemaVersion",
          "modelVersion",
          "config",
          "sun",
          "noise",
          "wind",
          "provenance",
          "limitations"
        ],
        "properties": {
          "schemaVersion": {
            "const": "1.0"
          },
          "modelVersion": {
            "type": "string"
          },
          "generatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "config": {
            "$ref": "#/components/schemas/Config"
          },
          "sun": {
            "type": "object"
          },
          "noise": {
            "type": [
              "object",
              "null"
            ]
          },
          "wind": {
            "type": [
              "object",
              "null"
            ]
          },
          "criteria": {
            "type": "object",
            "description": "Сценарное сравнение с ориентирами; compliance всегда not-assessed. noise: LAeq за час, reference, lmax=null, receiver. ventilation: referenceM3h, averageM3h, airQuality=not-assessed."
          },
          "construction": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "renovation": {
            "type": "object"
          },
          "facadeWindows": {
            "type": "object"
          },
          "provenance": {
            "type": "object"
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
