{
  "upstreams": {
    "api": {
      "name": "Weather Data Hub",
      "env": "WEATHER_DATA_HUB_URL",
      "base_url": "http://weather_data_hub:7000"
    },
    "map": {
      "name": "Martin Tiles",
      "env": "MARTIN_TILES_URL",
      "base_url": "https://tiles.weather.giscloud.ru"
    },
    "terrain": {
      "name": "Terrain Elevation",
      "env": "TERRAIN_ELEVATION_URL",
      "base_url": "https://terrain-elevation.mf.giscloud.ru"
    },
    "dispersion": {
      "name": "Dispersion Service",
      "env": "DISPERSION_SERVICE_URL",
      "base_url": "http://weather_data_hub:7000",
      "enabled": true
    },
    "sppi_wind": {
      "name": "SPPI Legacy Wind",
      "env": "SPPI_GRIB_URL",
      "base_url": "https://sppi.gis-master.ru/api/grib"
    },
    "geoserver_wind": {
      "name": "GeoServer Wind WMS",
      "env": "GEOSERVER_WMS_URL",
      "base_url": "http://127.0.0.1:8092/geoserver/grib/wms",
      "metadata_url": "http://127.0.0.1:8091/api/grib"
    }
  },
  "tabs": {
    "api": [
      {
        "title": "AI (Weather Data Hub)",
        "endpoints": [
          {
            "method": "GET",
            "path": "/api/ai/profiles",
            "summary": "Audience profiles, model and interface limits from Hub"
          },
          {
            "method": "POST",
            "path": "/api/ai/chat",
            "summary": "Chat through Hub with validated weather context"
          },
          {
            "method": "POST",
            "path": "/api/ai/chat/plain",
            "summary": "Plain model chat through the portal without added prompts or weather context"
          },
          {
            "method": "POST",
            "path": "/api/ai/point-explanation",
            "summary": "Audience-specific point weather explanation from Hub"
          }
        ]
      },
      {
        "title": "Service and Docs",
        "endpoints": [
          {
            "method": "GET",
            "path": "/healthcheck",
            "summary": "Service health check"
          },
          {
            "method": "GET",
            "path": "/api/openapi.json",
            "summary": "OpenAPI JSON document"
          },
          {
            "method": "GET",
            "path": "/api/docs",
            "summary": "Swagger UI documentation"
          },
          {
            "method": "GET",
            "path": "/api/foo",
            "summary": "Basic service status endpoint"
          },
          {
            "method": "POST",
            "path": "/api/create",
            "summary": "Create placeholder resource"
          }
        ]
      },
      {
        "title": "Meteo Data",
        "endpoints": [
          {
            "method": "POST",
            "path": "/api/weather/query",
            "summary": "Unified weather samples, features, daily forecasts, diagnostics and availability"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getSigwxUpdateDate",
            "summary": "SIGWX update timestamp by source"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getAerodromWeather",
            "summary": "Aerodrome weather by airport index"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/searchAerodromes",
            "summary": "Search places and aerodromes by name, alias, ICAO, or IATA"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/searchPlacesByCoordinates",
            "summary": "All geographic places and airports within a WGS84 radius"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/getTafByAirportAndDate",
            "summary": "TAF list by airport and date"
          },
          {
            "method": "POST",
            "path": "/api/Meteo/getTafByAirportsAndDate",
            "summary": "TAF list by airports and date"
          },
          {
            "method": "POST",
            "path": "/api/Meteo/getTafMapByAirportsAndDate",
            "summary": "Latest TAF map by airports and date"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/getMetarTafText",
            "summary": "METAR/SPECI/TAF raw texts after dateTime"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/getExternalMeteo",
            "summary": "METAR/SPECI GeoJSON by bbox or airport id"
          },
          {
            "method": "POST",
            "path": "/api/Meteo/getExternalMeteo",
            "summary": "METAR/SPECI GeoJSON by bbox or airport id"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/getWeatherBoard",
            "summary": "Airport weather board"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/militaryMeteoAer",
            "summary": "Legacy military weather board wrapper"
          },
          {
            "method": "POST",
            "path": "/api/Meteo/militaryMeteoAer",
            "summary": "Legacy military weather board wrapper"
          },
          {
            "method": "POST",
            "path": "/api/Meteo/getMetarTafByAirpotsList",
            "summary": "METAR and TAF raw text by airport list"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/internal/getSigmets",
            "summary": "Active SIGMET zones"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/internal/getAirmets",
            "summary": "Active AIRMET zones"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/internal/getGamets",
            "summary": "Active GAMET zones"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/internal/rb/getSigmets",
            "summary": "Active SIGMET zones from RB parser storage"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/internal/rb/getAirmets",
            "summary": "Active AIRMET zones from RB parser storage"
          },
          {
            "method": "GET",
            "path": "/api/Meteo/internal/rb/getGamets",
            "summary": "Active GAMET zones from RB parser storage"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getSigmet",
            "summary": "SIGMET by id"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getAirmet",
            "summary": "AIRMET by id"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getGamet",
            "summary": "GAMET by id"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getVa",
            "summary": "Volcanic ash advisory by id"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getTc",
            "summary": "Tropical cyclone advisory by id"
          },
          {
            "method": "POST",
            "path": "/api/meteoData/getSigmetGeometry",
            "summary": "Active SIGMET geometry list"
          },
          {
            "method": "POST",
            "path": "/api/meteoData/getSigmetList",
            "summary": "Active SIGMET list"
          },
          {
            "method": "POST",
            "path": "/api/meteoData/getAirmetGeometry",
            "summary": "Active AIRMET geometry list"
          },
          {
            "method": "POST",
            "path": "/api/meteoData/getGametGeometry",
            "summary": "Active GAMET geometry list"
          },
          {
            "method": "GET",
            "path": "/api/meteoData/getTcAdvisoryList",
            "summary": "Active tropical cyclone advisory list"
          }
        ]
      },
      {
        "title": "Bulletins",
        "endpoints": [
          {
            "method": "POST",
            "path": "/api/bulletins/point",
            "summary": "Create meteorological bulletin for a point or aerodrome"
          },
          {
            "method": "POST",
            "path": "/api/bulletins/route",
            "summary": "Create meteorological bulletin for a route"
          },
          {
            "method": "GET",
            "path": "/api/bulletins/nearest-aerodrome",
            "summary": "Resolve nearest aerodrome for point coordinates"
          },
          {
            "method": "POST",
            "path": "/api/bulletins/aerodrome-candidates",
            "summary": "Find aerodrome candidates for up to three circles with current METAR"
          },
          {
            "method": "GET",
            "path": "/api/bulletins/{id}",
            "summary": "Get stored meteorological bulletin by id"
          },
          {
            "method": "GET",
            "path": "/api/bulletins/{id}.html",
            "summary": "Get stored meteorological bulletin as HTML"
          },
          {
            "method": "GET",
            "path": "/api/bulletins/{id}.pdf",
            "summary": "Get stored meteorological bulletin as PDF"
          },
          {
            "method": "GET",
            "path": "/api/bulletins/{id}.md",
            "summary": "Get stored meteorological bulletin as Markdown"
          }
        ]
      },
      {
        "title": "GRIB",
        "endpoints": [
          {
            "method": "GET",
            "path": "/api/weather/raster-sources/{source}/dem",
            "summary": "8-bit RGB/RGBA DEM frame for client-side weather rendering"
          },
          {
            "method": "POST",
            "path": "/api/grib/getGribInfo",
            "summary": "GRIB metadata"
          },
          {
            "method": "POST",
            "path": "/api/Meteo/getGribInfo",
            "summary": "Legacy alias for GRIB metadata"
          },
          {
            "method": "GET",
            "path": "/api/grib/getGribLayersInfo",
            "summary": "GRIB layers metadata"
          },
          {
            "method": "GET",
            "path": "/api/grib/getGribLayersData",
            "summary": "GRIB layers as binary payload"
          },
          {
            "method": "GET",
            "path": "/api/grib/getGribLayersDataJson",
            "summary": "GRIB layers as JSON payload"
          },
          {
            "method": "POST",
            "path": "/api/grib/getSimpleRouteMeteoByGrib",
            "summary": "Meteo for route points"
          },
          {
            "method": "POST",
            "path": "/api/grib/getMfpGrib",
            "summary": "MFP GRIB data"
          },
          {
            "method": "POST",
            "path": "/api/grib/getGribForPoints",
            "summary": "GRIB forecast by points"
          },
          {
            "method": "POST",
            "path": "/api/grib/getSppiMeteoPoint",
            "summary": "SPPI meteo for point"
          },
          {
            "method": "POST",
            "path": "/api/grib/getSppiMeteoPoints",
            "summary": "SPPI meteo for points"
          },
          {
            "method": "POST",
            "path": "/api/grib/getMeteoByBbox",
            "summary": "Meteo by bounding box"
          },
          {
            "method": "POST",
            "path": "/api/grib/getMeteoByTypeInPoint",
            "summary": "Meteo by type in point"
          },
          {
            "method": "POST",
            "path": "/api/grib/wfs/wfs_keys_pb",
            "summary": "WFS keys protobuf payload"
          },
          {
            "method": "POST",
            "path": "/api/grib/wfs/wfs_grid_wind_pb",
            "summary": "WFS wind grid protobuf payload"
          },
          {
            "method": "POST",
            "path": "/api/grib/tms/getTileImage",
            "summary": "Get tile image"
          },
          {
            "method": "POST",
            "path": "/api/grib/tms/renderTileImage",
            "summary": "Render tile image"
          },
          {
            "method": "GET",
            "path": "/api/grib/tms/{type}/{forecast}/{height}/{z}/{x}/{y}.webp",
            "summary": "Get tile image by path params"
          },
          {
            "method": "GET",
            "path": "/api/grib/tms/reload",
            "summary": "Reload tile cache"
          },
          {
            "method": "GET",
            "path": "/api/grib/tms/testMem",
            "summary": "Warmup tile cache"
          },
          {
            "method": "GET",
            "path": "/api/grib/internal/reloadData",
            "summary": "Trigger GRIB data reload"
          }
        ]
      },
      {
        "title": "Operations and Legacy Capture",
        "endpoints": [
          {
            "method": "GET",
            "path": "/api/db-indexes/status",
            "summary": "Database index status"
          },
          {
            "method": "POST",
            "path": "/api/db-indexes/recheck",
            "summary": "Recheck database indexes"
          },
          {
            "method": "GET",
            "path": "/api/old-requests/catalog",
            "summary": "Captured request catalog"
          },
          {
            "method": "GET",
            "path": "/api/old-requests/entries/{id}",
            "summary": "Captured request details"
          },
          {
            "method": "GET",
            "path": "/api/old-requests/entries/{id}/body",
            "summary": "Captured request body"
          },
          {
            "method": "DELETE",
            "path": "/api/old-requests/entries/{id}",
            "summary": "Delete captured request"
          },
          {
            "method": "POST",
            "path": "/api/old-requests/entries/{id}/check",
            "summary": "Mark captured request as checked"
          },
          {
            "method": "POST",
            "path": "/api/old-requests/delete-errors",
            "summary": "Delete non-success captured requests"
          },
          {
            "method": "POST",
            "path": "/api/old-requests/groups/check",
            "summary": "Mark captured request group as checked"
          },
          {
            "method": "POST",
            "path": "/api/old-requests/compare",
            "summary": "Replay and compare captured request"
          },
          {
            "method": "POST",
            "path": "/api/old-requests/reports",
            "summary": "Create captured request report"
          }
        ]
      }
    ],
    "map": [
      {
        "title": "Styles and Assets",
        "endpoints": [
          {
            "method": "GET",
            "path": "/styles",
            "summary": "Available Martin style list"
          },
          {
            "method": "GET",
            "path": "/styles/{style_path}",
            "summary": "MapLibre style JSON with MARTIN_HOST interpolation"
          },
          {
            "method": "GET",
            "path": "/sprite/{sprite_file}",
            "summary": "Map sprite JSON or PNG assets"
          },
          {
            "method": "GET",
            "path": "/fonts/{fontstack}/{range}.pbf",
            "summary": "Map font glyph range"
          },
          {
            "method": "GET",
            "path": "/fonts.json",
            "summary": "Available font list"
          }
        ]
      },
      {
        "title": "Sources and Metadata",
        "endpoints": [
          {
            "method": "GET",
            "path": "/sources/{source}",
            "summary": "TileJSON and source metadata"
          },
          {
            "method": "GET",
            "path": "/sources/{source}/dem",
            "summary": "Martin RGB DEM producer endpoint; browser consumers use the Portal proxy"
          },
          {
            "method": "GET",
            "path": "/sources/{source}/ows",
            "summary": "GeoJSON OWS-style source query"
          },
          {
            "method": "GET",
            "path": "/api/weather/gamet-area",
            "summary": "Full geometry and polygon parts for one GAMET reference area"
          },
          {
            "method": "GET",
            "path": "/api/weather/profile",
            "summary": "Vertical GRIB profile for an aerogram or atmospheric time section"
          },
          {
            "method": "GET",
            "path": "/api/weather/profile-capabilities",
            "summary": "Available sounding model runs and times (metadata only)"
          },
          {
            "method": "GET",
            "path": "/api/weather/profile-series",
            "summary": "Vertical GRIB profile series from one model run"
          },
          {
            "method": "POST",
            "path": "/api/weather/route-profile",
            "summary": "Batch vertical GRIB profiles along one route and model run"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/meteorology/capabilities",
            "summary": "Dispersion meteorology field capability matrix"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/engines",
            "summary": "Available dispersion calculation engines"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/source-profiles",
            "summary": "Versioned dispersion source-term profiles"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/threshold-sets",
            "summary": "Versioned concentration threshold sets"
          },
          {
            "method": "POST",
            "path": "/api/dispersion/scenarios",
            "summary": "Create a Hub-owned dispersion scenario"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/scenarios/:id",
            "summary": "Read a Hub-owned dispersion scenario"
          },
          {
            "method": "PATCH",
            "path": "/api/dispersion/scenarios/:id",
            "summary": "Replace a Hub-owned dispersion scenario"
          },
          {
            "method": "DELETE",
            "path": "/api/dispersion/scenarios/:id",
            "summary": "Delete a dispersion scenario without runs"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/scenarios/:id/runs",
            "summary": "List dispersion runs for a scenario"
          },
          {
            "method": "POST",
            "path": "/api/dispersion/scenarios/:id/runs",
            "summary": "Start a Hub dispersion calculation"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id",
            "summary": "Read dispersion run status"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/times",
            "summary": "Read published dispersion frame times"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/manifest",
            "summary": "Read dispersion result manifest"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/provenance",
            "summary": "Read dispersion provenance"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/mass-balance",
            "summary": "Read dispersion mass balance"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/point",
            "summary": "Query one calculated dispersion value"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/profile",
            "summary": "Query a calculated vertical profile"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/wind-field",
            "summary": "Read a real u/v field for optional visual particles"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/tiles/:metric/:species/:time/:z/:x/:y.png",
            "summary": "Read a dispersion raster tile"
          },
          {
            "method": "GET",
            "path": "/api/dispersion/runs/:id/contours/:metric/:species/:time/:z/:x/:y.geojson",
            "summary": "Read a dispersion threshold tile"
          },
          {
            "method": "POST",
            "path": "/api/weather/balloon-trajectory",
            "summary": "Wind-integrated balloon or sonde trajectory"
          },
          {
            "method": "GET",
            "path": "/api/weather/hot-air-balloon-profiles",
            "summary": "Versioned hot-air-balloon aircraft profiles"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-loading",
            "summary": "Hot-air-balloon loading and static lift check"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-flight-jobs",
            "summary": "Start a thermal and wind-drift flight calculation"
          },
          {
            "method": "GET",
            "path": "/api/weather/hot-air-balloon-flight-jobs/:id",
            "summary": "Read hot-air-balloon flight progress and result"
          },
          {
            "method": "DELETE",
            "path": "/api/weather/hot-air-balloon-flight-jobs/:id",
            "summary": "Cancel a hot-air-balloon flight calculation"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-ensemble-jobs",
            "summary": "Start a 31-member GEFS hot-air-balloon ensemble calculation"
          },
          {
            "method": "GET",
            "path": "/api/weather/hot-air-balloon-ensemble-jobs/:id",
            "summary": "Read GEFS hot-air-balloon ensemble progress and result"
          },
          {
            "method": "DELETE",
            "path": "/api/weather/hot-air-balloon-ensemble-jobs/:id",
            "summary": "Cancel a GEFS hot-air-balloon ensemble calculation"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-calibration-jobs",
            "summary": "Compare an observed hot-air-balloon track with a completed forecast"
          },
          {
            "method": "GET",
            "path": "/api/weather/hot-air-balloon-calibration-jobs/:id",
            "summary": "Read calibration evaluation progress and result"
          },
          {
            "method": "DELETE",
            "path": "/api/weather/hot-air-balloon-calibration-jobs/:id",
            "summary": "Cancel a calibration evaluation"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-calibration-reviews",
            "summary": "Aggregate completed calibration evaluations for review"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-calibration-approvals",
            "summary": "Explicitly approve a reviewed calibration version"
          },
          {
            "method": "GET",
            "path": "/api/weather/hot-air-balloon-calibration-approvals/:id",
            "summary": "Read an approved calibration version"
          },
          {
            "method": "POST",
            "path": "/api/weather/hot-air-balloon-control-jobs",
            "summary": "Branch a completed flight with new pilot commands"
          },
          {
            "method": "POST",
            "path": "/api/weather/balloon-ensemble-jobs",
            "summary": "Start a 31-member GEFS balloon or sonde trajectory calculation"
          },
          {
            "method": "GET",
            "path": "/api/weather/balloon-ensemble-jobs/:id",
            "summary": "Read GEFS calculation progress and result"
          },
          {
            "method": "DELETE",
            "path": "/api/weather/balloon-ensemble-jobs/:id",
            "summary": "Cancel a GEFS calculation"
          },
          {
            "method": "POST",
            "path": "/api/weather/balloon-control-ensemble-jobs",
            "summary": "Check a fixed altitude-control schedule across physical GEFS members"
          },
          {
            "method": "GET",
            "path": "/api/weather/balloon-control-ensemble-jobs/:id",
            "summary": "Read GEFS control-plan stability progress and result"
          },
          {
            "method": "DELETE",
            "path": "/api/weather/balloon-control-ensemble-jobs/:id",
            "summary": "Cancel a GEFS control-plan stability calculation"
          },
          {
            "method": "POST",
            "path": "/api/weather/balloon-telemetry-replay-jobs",
            "summary": "Restart a balloon forecast from accepted telemetry"
          },
          {
            "method": "GET",
            "path": "/api/weather/balloon-telemetry-replay-jobs/:id",
            "summary": "Read telemetry replay progress and result"
          },
          {
            "method": "DELETE",
            "path": "/api/weather/balloon-telemetry-replay-jobs/:id",
            "summary": "Cancel a telemetry replay calculation"
          },
          {
            "method": "POST",
            "path": "/api/terrain/profile",
            "summary": "ArcGIS Elevation3D zoom-10 heights for ordered route points"
          },
          {
            "method": "GET",
            "path": "/api/weather/aerodrome-report",
            "summary": "Current METAR/SPECI and TAF for an aerodrome and requested ETA"
          },
          {
            "method": "GET",
            "path": "/api/weather/airspace-structure",
            "summary": "Active special OPMET conditions for a point"
          },
          {
            "method": "POST",
            "path": "/api/weather/opmet-profile",
            "summary": "ETA-matched OPMET conditions for profile points"
          },
          {
            "method": "GET",
            "path": "/get_info/{feature_id}",
            "summary": "NOTAM feature details"
          }
        ]
      },
      {
        "title": "Tiles and Images",
        "endpoints": [
          {
            "method": "GET",
            "path": "/api/v1/map/geoserver-wind/options",
            "summary": "Available GeoServer wind forecast times and levels"
          },
          {
            "method": "GET",
            "path": "/api/v1/map/geoserver-wind/point",
            "summary": "GeoServer/GRIB wind speed and direction at a point"
          },
          {
            "method": "GET",
            "path": "/api/v1/map/geoserver-wind/{forecast}/{level}/{z}/{x}/{y}.png",
            "summary": "Same-origin GeoServer WMS wind tile proxy"
          },
          {
            "method": "GET",
            "path": "/api/v1/map/sppi-wind/options",
            "summary": "Available SPPI wind forecast times and pressure levels"
          },
          {
            "method": "GET",
            "path": "/api/v1/map/sppi-wind/{forecast}/{height}/{z}/{x}/{y}.webp",
            "summary": "Same-origin legacy SPPI wind tile proxy"
          },
          {
            "method": "GET",
            "path": "/rb_tiles/{source}/{z}/{x}/{y}",
            "summary": "Vector tile, raster tile, image tile, or DEM tile by source"
          },
          {
            "method": "GET",
            "path": "/rb_tiles/{source}",
            "summary": "Single image weather raster source"
          },
          {
            "method": "GET",
            "path": "/dem/tiles.json",
            "summary": "Demo raster-dem TileJSON"
          },
          {
            "method": "GET",
            "path": "/dem/tiles/{z}/{x}/{y}.png",
            "summary": "Demo raster-dem tile image"
          }
        ]
      },
      {
        "title": "Spatial Queries",
        "endpoints": [
          {
            "method": "GET",
            "path": "/rb_within/{source}",
            "summary": "Features within radius of lat/lon"
          },
          {
            "method": "POST",
            "path": "/rb_intersects/{source}",
            "summary": "Features intersecting WKT geometry"
          }
        ]
      },
      {
        "title": "Map Debug Pages",
        "endpoints": [
          {
            "method": "GET",
            "path": "/map",
            "summary": "Martin debug MapLibre page"
          },
          {
            "method": "GET",
            "path": "/map6",
            "summary": "MapLibre preview page for selected style URL"
          }
        ]
      }
    ]
  }
}