{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/smarterweather-com/json-schema/smarterweather-com-weather-schema.json",
  "title": "Weather",
  "description": "Composite blended weather payload returned by `GET /v1/weather`.\nSection presence is controlled by the `include` query parameter;\n`location` and `gis` are always present. With the default `view=detailed` and no `fields`, every section\nthat is present is the complete shape documented below. `view=concise`\nand `fields` project the members of `hourly[]` and `daily[]` rows\n(every other section stays complete), and `hourly_step` thins\n`hourly[]` rows; see \"Response density, sampling and sparse\nfieldsets\" on `GET /v1/weather`. A shaped body is always a\nkey-subset of the detailed body for the same request.\n",
  "x-generated": "2026-10-30",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/smarterweather-com-openapi.yaml#/components/schemas/Weather",
  "type": "object",
  "required": [
    "location",
    "gis",
    "meta",
    "attribution"
  ],
  "properties": {
    "location": {
      "$ref": "#/$defs/Location"
    },
    "current": {
      "$ref": "#/$defs/Current"
    },
    "hourly": {
      "type": "array",
      "description": "Hourly forecast, ordered ascending by `time`.",
      "items": {
        "$ref": "#/$defs/Hourly"
      }
    },
    "daily": {
      "type": "array",
      "description": "Daily forecast, ordered ascending by `date`.",
      "items": {
        "$ref": "#/$defs/Daily"
      }
    },
    "alerts": {
      "type": "array",
      "description": "Active NWS alerts for the location, present whenever the section is included and its fetch succeeded. `[]` means no alerts were in effect in the latest alerts snapshot, which can trail `meta.generated_at` by up to about 30 seconds. Absent when the section is not included, or when its fetch failed (a `meta.warnings` entry with `section: alerts` says so). A stale replay (`X-SW-Stale: 1` header, or a `stale_response` warning on a batch row) repeats the alerts as of its own `meta.generated_at`, not the current set.",
      "items": {
        "$ref": "#/$defs/Alert"
      }
    },
    "outlooks": {
      "type": "array",
      "description": "SPC convective outlooks active for the location, present whenever the section is included and its fetch succeeded. `[]` means no outlook was in effect in the latest outlooks snapshot, which can trail `meta.generated_at` by up to about 30 seconds. Absent when the section is not included, or when its fetch failed (a `meta.warnings` entry with `section: outlooks` says so). A stale replay repeats the outlooks as of its own `meta.generated_at`.",
      "items": {
        "$ref": "#/$defs/Outlook"
      }
    },
    "astro": {
      "$ref": "#/$defs/Astro"
    },
    "gis": {
      "$ref": "#/$defs/GIS"
    },
    "units": {
      "$ref": "#/$defs/Units"
    },
    "attribution": {
      "$ref": "#/$defs/Attribution"
    },
    "narrations": {
      "$ref": "#/$defs/Narrations"
    },
    "guidance": {
      "$ref": "#/$defs/Guidance"
    },
    "discussions": {
      "$ref": "#/$defs/Discussions"
    },
    "nowcast": {
      "$ref": "#/$defs/Nowcast"
    },
    "climate": {
      "allOf": [
        {
          "$ref": "#/$defs/ClimateNormals"
        }
      ],
      "description": "Day-of-year climate normals for the location, populated when\n`include=climate` is requested. Identical in shape to the\n`GET /v1/climate/normals` body.\n\nThis block is the normals themselves — what is typical here. For\nhow the forecast compares to them, read `daily[].climate`, which\ncarries the same normals joined to each forecast day along with\nthe signed departure.\n\nOmitted when no station with normals is near the point; that case\nreports `coverage_limited` in `meta.warnings[]` rather than\nfailing the request.\n"
    },
    "metar": {
      "$ref": "#/$defs/Metar"
    },
    "meta": {
      "$ref": "#/$defs/Meta"
    }
  },
  "$defs": {
    "AFDGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Area Forecast Discussion: structured fields plus optional raw bulletin\n`body`.\n",
      "properties": {
        "synopsis": {
          "type": "string"
        },
        "key_takeaway": {
          "type": "string"
        },
        "confidence": {
          "type": "string",
          "description": "Forecaster-stated confidence level (free text)."
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "wfo": {
          "type": "string",
          "description": "Issuing Weather Forecast Office identifier."
        },
        "body": {
          "type": "string",
          "description": "Full AFD bulletin text when available."
        }
      }
    },
    "Alert": {
      "type": "object",
      "description": "NWS weather alert active for the requested location.",
      "required": [
        "id",
        "event",
        "severity",
        "urgency",
        "certainty",
        "headline",
        "sender",
        "status",
        "onset",
        "ends",
        "effective",
        "expires",
        "expiration"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique alert identifier (the NWS message id). Pass it to\n`GET /v1/alerts/{id}` for the full alert.\n"
        },
        "event": {
          "type": "string"
        },
        "severity": {
          "type": "string",
          "enum": [
            "Extreme",
            "Severe",
            "Moderate",
            "Minor",
            "Unknown"
          ]
        },
        "urgency": {
          "type": "string",
          "enum": [
            "Immediate",
            "Expected",
            "Future",
            "Past",
            "Unknown"
          ]
        },
        "certainty": {
          "type": "string",
          "enum": [
            "Observed",
            "Likely",
            "Possible",
            "Unlikely",
            "Unknown"
          ]
        },
        "headline": {
          "type": "string"
        },
        "sender": {
          "type": "string",
          "description": "Issuing NWS office identifier."
        },
        "status": {
          "type": "string",
          "description": "Alert lifecycle status (e.g., `Actual`, `Exercise`, `Test`)."
        },
        "onset": {
          "type": "string",
          "format": "date-time"
        },
        "ends": {
          "type": "string",
          "format": "date-time"
        },
        "effective": {
          "type": "string",
          "format": "date-time"
        },
        "expires": {
          "type": "string",
          "format": "date-time"
        },
        "expiration": {
          "type": "string",
          "format": "date-time",
          "description": "Final expiration time used for cache invalidation."
        },
        "area_desc": {
          "type": "string",
          "description": "Human-readable affected area."
        },
        "highlights": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          },
          "description": "Map of salient callouts extracted from the alert text\n(e.g., `{\"hazard\": \"Strong winds\", \"impact\": \"Loose debris possible\"}`).\n"
        }
      }
    },
    "Astro": {
      "type": "object",
      "description": "Astronomical data for the requested location and date.",
      "required": [
        "day_length",
        "daylight_length",
        "moon_phase",
        "moon_phase_name",
        "moon_illumination"
      ],
      "properties": {
        "civil_dawn": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Null when civil twilight does not begin or end on this date."
        },
        "sunrise": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Null on polar day and night (see `polar`)."
        },
        "solar_noon": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "sunset": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Null on polar day and night (see `polar`)."
        },
        "civil_dusk": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Null when civil twilight does not begin or end on this date."
        },
        "day_length": {
          "type": "integer",
          "minimum": 0,
          "description": "Seconds from sunrise to sunset; 86400 under midnight sun, 0 in polar night."
        },
        "daylight_length": {
          "type": "integer",
          "minimum": 0,
          "description": "Seconds from civil dawn to civil dusk; 86400 when civil twilight never ends (white nights, midnight sun). 0 when there is no civil daylight or it is unknown; see civil_dawn, civil_dusk and polar."
        },
        "moonrise": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Null when no moonrise occurs on this date."
        },
        "moonset": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Null when no moonset occurs on this date."
        },
        "moon_phase": {
          "type": "number",
          "format": "double",
          "minimum": 0,
          "maximum": 1,
          "description": "Moon phase (0 = new, 0.5 = full, 1 = new)."
        },
        "moon_phase_name": {
          "type": "string",
          "enum": [
            "New Moon",
            "Waxing Crescent",
            "First Quarter",
            "Waxing Gibbous",
            "Full Moon",
            "Waning Gibbous",
            "Last Quarter",
            "Waning Crescent"
          ]
        },
        "moon_illumination": {
          "type": "number",
          "format": "double",
          "minimum": 0,
          "maximum": 1
        },
        "polar": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "midnight_sun",
            "polar_night",
            null
          ],
          "description": "Set when the sun neither rises nor sets on this date: midnight_sun (above the horizon all day; day_length is 86400) or polar_night (below it all day; day_length is 0). sunrise and sunset are then null. Null when the sun rises and sets."
        },
        "sky": {
          "x-stability": "experimental",
          "allOf": [
            {
              "$ref": "#/$defs/Sky"
            }
          ],
          "description": "Sunrise and sunset color forecast for this date, present only\nwhen `include=sky` is requested, enabled for the API key, and a\nforecast exists for the date.\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n"
        }
      }
    },
    "Attribution": {
      "type": "object",
      "description": "Attribution for the data sources that contributed to this response.\n`sources` is an explicit list of canonical identifiers suitable\nfor compliance checklists; `notice` is a human-readable catch-all\ncovering licensing / credit language.\n",
      "required": [
        "sources",
        "notice"
      ],
      "properties": {
        "sources": {
          "type": "array",
          "description": "Canonical source identifiers that contributed data. `EPA AirNow`\nis listed only when the body as returned carries an air-quality\nvalue (`current.aqi`, `pm25`, `ozone` or `pm10`, `hourly[].aqi`,\n`daily[].aqi_max`, `daily[].day.aqi` or `daily[].night.aqi`), so a\n`view` or `fields` selection that drops those columns drops it.\n",
          "items": {
            "type": "string"
          }
        },
        "notice": {
          "type": "string",
          "description": "Human-readable attribution notice."
        }
      }
    },
    "ClimateNormals": {
      "type": "object",
      "description": "Day-of-year climate normals for a point, read from the gridded normals\nacross CONUS and otherwise from the nearest station with records. The\nresolved station is always named, whichever supplied the values.\n",
      "required": [
        "station",
        "meta",
        "days"
      ],
      "properties": {
        "station": {
          "$ref": "#/$defs/ClimateNormalsStation"
        },
        "meta": {
          "$ref": "#/$defs/ClimateNormalsMeta"
        },
        "days": {
          "type": "array",
          "description": "Per-day normals series, in requested date order.",
          "items": {
            "$ref": "#/$defs/ClimateNormalsDay"
          }
        },
        "location": {
          "$ref": "#/$defs/Location",
          "description": "Resolved location, as `/v1/weather` returns it. Present when the\nplace was resolved: always for a `zip` or `location` request, and\nfor `lat`/`lon` when the coverage check's lookup answered in time\n(omitted when that check fails open).\n"
        }
      }
    },
    "ClimateNormalsDay": {
      "type": "object",
      "description": "One day-of-year slice. Fields are omitted when no normal is available,\nwhich is not the same as a value of zero. `normal_precip` is\nadditionally omitted on any station-sourced day — see its description.\n",
      "required": [
        "date",
        "month",
        "day"
      ],
      "properties": {
        "date": {
          "type": "string",
          "format": "date",
          "description": "Calendar date this slice was requested for."
        },
        "month": {
          "type": "integer",
          "description": "Month of the day-of-year slice (1-12)."
        },
        "day": {
          "type": "integer",
          "description": "Day of month (1-31)."
        },
        "normal_high": {
          "type": "number",
          "format": "double",
          "description": "Average daily maximum temperature."
        },
        "normal_low": {
          "type": "number",
          "format": "double",
          "description": "Average daily minimum temperature."
        },
        "normal_mean": {
          "type": "number",
          "format": "double",
          "description": "Average daily mean temperature."
        },
        "normal_high_stddev": {
          "type": "number",
          "format": "double",
          "description": "Standard deviation of the daily maximum. An interval, so it scales\nwithout the freezing-point offset: 3 °C of spread is 5.4 °F.\n\nAlways from the nearest station, including on a day whose\ntemperatures came from the grid — the gridded normals publish no\nstandard deviations. On such a day the temperatures describe your\npoint while this describes the named station, so read\n`station.distance_km` before narrating \"unusually warm\" off this\nspread. The same holds for `normal_low_stddev`,\n`normal_mean_stddev`, `years` and `completeness_flag`.\n"
        },
        "normal_low_stddev": {
          "type": "number",
          "format": "double",
          "description": "Standard deviation of the daily minimum. Station-sourced on the\nsame terms as `normal_high_stddev`.\n"
        },
        "normal_mean_stddev": {
          "type": "number",
          "format": "double",
          "description": "Standard deviation of the daily mean. Station-sourced on the same\nterms as `normal_high_stddev`.\n"
        },
        "normal_precip": {
          "type": "number",
          "format": "double",
          "description": "Average daily precipitation amount — inches when `unit=imperial`,\nmillimetres when `unit=metric`.\n\nThe exact inverse of `normal_high_stddev`: that is always\nstation-sourced, this is always grid-sourced. The station product\npublishes no daily precipitation normal at all, so this field comes\nonly from the gridded CONUS product and is **omitted** on any day\nwhose `source` is `station` — which includes every point outside\nCONUS. A series over Alaska or Hawaii carries temperatures with no\nprecipitation until gridded normals exist there.\n\n`0` is a real value and is not the same as omitted: a daily normal\nis a 30-year mean spread across wet and dry days, so much of the\narid West is a few hundredths of an inch. Do not treat a present\nzero as missing data.\n"
        },
        "years": {
          "type": "integer",
          "description": "Years of the base period contributing to this day, at the station.\nStation-sourced on the same terms as `normal_high_stddev`, so it\ndoes not describe the depth of a gridded value.\n"
        },
        "completeness_flag": {
          "type": "string",
          "description": "NCEI per-value completeness code, for the station. Station-sourced\non the same terms as `normal_high_stddev`.\n"
        }
      }
    },
    "ClimateNormalsMeta": {
      "type": "object",
      "description": "Provenance for a normals response.",
      "required": [
        "source",
        "unit"
      ],
      "properties": {
        "normals_period": {
          "type": "string",
          "description": "Base period the normals were computed over."
        },
        "source": {
          "type": "string",
          "description": "Where the values came from: `station` for the NCEI station\nproduct, `grid` for gridded normals, `mixed` when a series draws\non both.\n"
        },
        "normals_method": {
          "type": "string",
          "enum": [
            "official",
            "derived"
          ],
          "description": "How the normals were produced, which `source` does not answer.\n`official` means the provider publishes them as a normals product\n-- true of both the NCEI station normals and the NCEI gridded\nnormals, so a station/grid mix is still wholly official.\n`derived` means they were computed from raw climate data, which is\na weaker claim a client should qualify. Absent when upstream does\nnot report it.\n"
        },
        "unit": {
          "type": "string",
          "enum": [
            "imperial",
            "metric"
          ],
          "description": "Unit system applied to the values: °F and inches for `imperial`,\n°C and millimetres for `metric`.\n"
        },
        "approximate": {
          "type": "boolean",
          "description": "True when a station more than 50 km away supplied values that are\nactually being served — `station.distance_km` says how far.\n\nIt is not a plain distance test. A `grid` day is the requested\npoint's own normal, so no station distance makes it approximate,\nand a series whose `source` is `grid` is never flagged however far\nthe named station sits. A `mixed` series is flagged, because the\nstation-sourced days in it genuinely are approximations.\n"
        },
        "missing_days": {
          "type": "integer",
          "description": "How many days in the requested range no normal is available for,\nfrom either the grid or the station. Those days are still present\nin `days` with their temperature fields omitted.\n"
        }
      }
    },
    "ClimateNormalsStation": {
      "type": "object",
      "description": "The station a normals series was drawn from. Normals describe this\nstation, not necessarily the point you asked about.\n",
      "required": [
        "station_id",
        "latitude",
        "longitude",
        "distance_km"
      ],
      "properties": {
        "station_id": {
          "type": "string",
          "description": "GHCN station identifier."
        },
        "name": {
          "type": "string",
          "description": "Station name as published by NCEI."
        },
        "state": {
          "type": "string",
          "description": "Two-letter state or territory code."
        },
        "latitude": {
          "type": "number",
          "format": "double",
          "description": "Station latitude."
        },
        "longitude": {
          "type": "number",
          "format": "double",
          "description": "Station longitude."
        },
        "elevation_m": {
          "type": "number",
          "format": "double",
          "description": "Station elevation in metres. Worth comparing against your point:\nan elevation difference biases normals more than distance does.\n"
        },
        "distance_km": {
          "type": "number",
          "format": "double",
          "description": "Great-circle distance from the requested point to the station,\nrounded to 0.1 km. The single number that says how much to trust\nthis series.\n"
        }
      }
    },
    "Current": {
      "type": "object",
      "description": "Current weather conditions derived from the RTMA + MRMS blend.\n",
      "required": [
        "time",
        "conditions",
        "icon"
      ],
      "properties": {
        "time": {
          "type": "string",
          "format": "date-time",
          "description": "Observation valid time (ISO 8601, UTC) of the analysis behind `temperature`, or the response time when no fresh observation is available."
        },
        "temperature": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Temperature (unit per `units.temperature`). Null only when no source in the current chain resolves it."
        },
        "feels_like": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Feels-like temperature: the air temperature unless wind chill (50°F or below, wind 3 mph or more) or heat index (80°F or above, relative humidity 40% or more) applies. Null when the index that applies lacks its input and no forecast apparent temperature covers now; never derived from an unknown value."
        },
        "humidity": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Relative humidity, percent. Null only when no source in the current chain resolves it."
        },
        "dew_point": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Dew point (unit per `units.temperature`). Null only when no source in the current chain resolves it, or its derivation lacks the temperature or humidity."
        },
        "pressure": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Sea-level pressure (unit per `units.pressure`): the quantity US NWS stations report in the METAR SLP group. From the observation-anchored RTMA analysis, its station pressure reduced to sea level with the WMO CIMO Guide 3.11 formula and the FMH-1 12-hour mean temperature; else the GFS mean-sea-level pressure field at now. Not the altimeter setting and not station pressure. Null only when neither source is fresh."
        },
        "wind_speed": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Sustained wind speed; `0` is calm (below 1 knot, FMH-1 5.5.2: served as `0` in every unit system). Null only when no source in the current chain resolves it."
        },
        "wind_gust": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Wind gust, served only next to a non-calm `wind_speed`. Null when no source carries a gust, or the wind is calm or unknown."
        },
        "wind_direction": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 360,
          "description": "Wind direction in degrees (0 = N, 90 = E). Null when calm (`wind_speed` 0) and when no source resolves it."
        },
        "visibility": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Visibility distance (null when no station or model value is available)."
        },
        "cloud_cover": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Cloud cover, percent. Null only when no source in the current chain resolves it."
        },
        "cloud_ceiling": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "description": "Analysis cloud-base height above ground (RTMA/URMA), in the `units.altitude` unit. Null when no ceiling is reported (clear or high thin cloud) or outside analysis coverage."
        },
        "cloud_cover_low": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "HRRR low-cloud-layer cover, percent (CONUS). Present only with `cloud_cover_mid` and `cloud_cover_high` (all three numbers or all three null). Null outside HRRR coverage or before the first post-deploy cycle."
        },
        "cloud_cover_mid": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "HRRR mid-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`."
        },
        "cloud_cover_high": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "HRRR high-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`."
        },
        "uv_index": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "aqi": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Air-quality index (US EPA scale): usually 0-500, can exceed 500\nin extreme events (\"Beyond the AQI\"). `null` when AirNow has no\nvalue at the point, or when its latest observation is older than\nthe source's freshness limit (currently 3 hours for AirNow). The\nstale case also adds a `stale_source` warning to `meta.warnings`\n(`section: current`, `product_id: airquality_combined`), which\ntells the two apart. The product's age is the `meta.freshness`\nentry with the same `product_id`. A reported 0 is currently also\nserved as `null`: AirNow stores cells outside its coverage as 0,\nso a 0 cannot yet be told from no data.\n"
        },
        "aqi_category": {
          "type": "string",
          "description": "Human-readable AQI category (e.g., `\"Good\"`, `\"Moderate\"`,\n`\"Unhealthy for Sensitive Groups\"`). Omitted when AQI is\nunavailable.\n"
        },
        "pm25": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "AirNow AQI sub-index for PM2.5, on the same scale as `aqi`:\nusually 0-500, can exceed 500 in extreme events. Integer-valued\nat the source; a value may show a small fractional error, so\nround to a whole number. Not a concentration: the field name\nnames the pollutant and is kept for compatibility. `null` when\nAirNow has no value at the point, when its latest hour is stale\n(reported by a `stale_source` warning, see `aqi`), or when the\nreported value is 0 (currently indistinguishable from no data,\nsee `aqi`).\n"
        },
        "ozone": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "AirNow AQI sub-index for ozone, on the same scale as `aqi`:\nusually 0-500, can exceed 500 in extreme events. Integer-valued\nat the source; a value may show a small fractional error, so\nround to a whole number. Not a concentration: the field name\nnames the pollutant and is kept for compatibility. `null` when\nAirNow has no value at the point, when its latest hour is stale\n(reported by a `stale_source` warning, see `aqi`), or when the\nreported value is 0 (currently indistinguishable from no data,\nsee `aqi`).\n"
        },
        "pm10": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "AirNow AQI sub-index for PM10, on the same scale as `aqi`:\nusually 0-500, can exceed 500 in extreme events. Integer-valued\nat the source; a value may show a small fractional error, so\nround to a whole number. Not a concentration: the field name\nnames the pollutant and is kept for compatibility. `null` when\nAirNow has no value at the point, when its latest hour is stale\n(reported by a `stale_source` warning, see `aqi`), or when the\nreported value is 0 (currently indistinguishable from no data,\nsee `aqi`).\n"
        },
        "precip_rate": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Current precipitation rate per hour. `0` when dry (`precip_type` `none`); null when neither radar nor a forecast source covers the point now."
        },
        "precip_type": {
          "type": "string",
          "enum": [
            "none",
            "rain",
            "snow",
            "mix",
            "freezing_rain",
            "hail",
            "unknown"
          ],
          "description": "Current precipitation type: MRMS radar where it covers the point,\nelse the current chain's forecast type at now, which is served\nonly with a measurable rate (0.01 in per hour); below it, `none`.\n`none` is a known dry reading. Omitted when neither radar nor a\nforecast source covers the point now. Radar serves\n`freezing_rain` and `mix` where its rain or snow is reclassified\nby the analysis wet-bulb. `mix` is rain and snow, ice pellets,\nor a wintry mix; `hail` and `unknown` are reserved and not\ncurrently served.\n"
        },
        "precip_24h": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "x-stability": "experimental",
          "description": "Observed precipitation accumulated over the trailing 24 hours,\nfrom MRMS MultiSensor QPE Pass 2. The window ends at the\nproduct's valid time, listed under `meta.freshness`. Always\nmillimetres regardless of `units`. CONUS only; `null` outside\nCONUS, when no sample is available, or when the sample's valid\ntime is older than the product's hourly-cadence staleness\nwindow (currently 3 hours, the same window `/v1/point` uses to\nmark it approximate). Source resolution is about 2 mm.\n\n**Experimental.** May change without notice: excluded from the\n90-day breaking-change commitment.\n"
        },
        "lightning_density": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Lightning strike density (strikes per unit area per time window)."
        },
        "lightning_prob": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 100,
          "description": "Lightning probability, percent."
        },
        "conditions": {
          "type": "string",
          "description": "Human-readable weather description. Empty string when neither the sky (cloud cover) nor precipitation is known."
        },
        "icon": {
          "type": "string",
          "description": "Icon slug for UI rendering. Empty string exactly when `conditions` is."
        },
        "severe": {
          "type": "boolean",
          "description": "True when severe conditions are present or imminent.",
          "default": false
        }
      }
    },
    "Daily": {
      "type": "object",
      "description": "Single daily forecast entry. Per-period weather (temperature, wind,\nprecipitation, narrative) lives in `day` / `night`, the row's local\n06:00-18:00 and 18:00-06:00 halves, so a row spans 06:00 local on\n`date` to 06:00 local the next day rather than the calendar day.\nPrecipitation, snow and ice totals are built on that window; the\nother top-level aggregates come from the daily model product for\n`date`, sub-daily guidance, or both, and promise no window. The row\nthat holds the response time may cover only the remainder of its\nwindow, and the overnight row before 06:00 (`relative_day: -1`)\ncarries only its overnight portion. A row built from the hourly\nforecast alone (no daily product and no 6 h precipitation buckets\nin the response) uses the local calendar day for its top-level\nvalues instead. `high` is the daytime maximum (on hourly-only rows,\nthe calendar day's maximum); `low` is the overnight minimum described\non that field and is not bounded by the row's window.\n",
      "required": [
        "date"
      ],
      "properties": {
        "date": {
          "type": "string",
          "format": "date",
          "description": "Forecast date (ISO 8601, local to the location's timezone)."
        },
        "high": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Daytime maximum (on hourly-only rows, the calendar day's maximum).\nOn today's row (`relative_day: 0`) it takes the observed (RTMA)\ndaily maximum into account when one is available: it is not\nreported below it, and from 18:00 local, while the analysis is\ncurrent, it is that observation combined with the forecast for the\nhours left. The observed day is a fixed-offset day per region\n(CONUS UTC-6, Alaska UTC-9, Hawaii UTC-10), so outside those\noffsets it starts or ends up to two hours away from local\nmidnight. On the `relative_day: -1` overnight row, whose daytime\nis past, it is that date's observed maximum when one is available\nand not below `low`, else the forecast cycle's when it still\ncarries one. Null when no source carries the value, and on\ntoday's row before 18:00 local when only the observation is\navailable and the forecast hours left do not reach the afternoon\n(a morning maximum is not the day's). It is never estimated from\n`low`.\n"
        },
        "low": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Overnight minimum temperature for `date`: the lower of the\nmorning-of-`date` and night-after-`date` minima where the source\ncarries both. The source decides the exact hours (NBM MinT\nperiods, or hourly minima over those night hours); it is not\nbounded by the row's 06:00-06:00 window. Null when no source\ncarries a minimum for the date (for example past the forecast's\nminimum-temperature horizon): it is never estimated from `high`.\nReported on the `relative_day: -1` row whenever a source carries\nit, since that row's remaining overnight hours are still ahead.\n"
        },
        "conditions": {
          "type": "string"
        },
        "icon": {
          "type": "string"
        },
        "severe": {
          "type": "boolean",
          "default": false
        },
        "precip_probability": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability of measurable precipitation for the row; `0` is a dry row. Null only when no forecast source covers the row."
        },
        "precip_total": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Null when no forecast source provides precipitation for the row."
        },
        "snow_total": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Null when `precip_total` is null and no source provides snowfall for the row."
        },
        "ice_total": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Null when `precip_total` is null and no source provides ice for the row."
        },
        "wind_speed_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Maximum sustained wind for the row; `0` is calm (below 1 knot, FMH-1 5.5.2: served as `0` in every unit system). Null only when no forecast source covers the row."
        },
        "wind_gust_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Maximum gust for the row, served only next to a non-calm `wind_speed_max`. Null when no source carries a gust, or the row's wind is calm or unknown."
        },
        "wind_direction": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 360,
          "description": "Dominant wind direction in degrees (0 = N). Null when calm (`wind_speed_max` 0) and when no forecast source covers the row."
        },
        "humidity_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Mean relative humidity, percent. Null only when no forecast source covers the row."
        },
        "cloud_cover_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Mean cloud cover, percent; `0` is clear. Null only when no forecast source covers the row."
        },
        "uv_index_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "The row's maximum UV index. It is worked out from the hourly\nforecast the server builds for the request, `hourly_hours` long,\neven when `include` omits `hourly`. On a day that forecast covers\nfrom 06:00 to 18:00 local, it is the highest hourly UV index. On\nother days it is an estimate from the UV index at solar noon and\nthe forecast cloud cover, raised to any higher hourly value. Late\nin the day, today's value reflects the remaining forecast, not the\nobserved peak: after 18:00 local the estimate can use tonight's\nforecast cloud, so a clear day followed by a cloudy evening can\nreport a low maximum. Any row's value can change between requests\nas the hours the forecast covers move. Null when it can't be\ndetermined.\n"
        },
        "cape_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Maximum convective available potential energy (J/kg). Null only when no forecast source covers the row."
        },
        "dew_point_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "dew_point_min": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "pressure_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Mean sea-level pressure (unit per `units.pressure`) over the row's remaining 06:00-06:00 window: the quantity US NWS stations report in the METAR SLP group, from the GFS mean-sea-level pressure field. Not the altimeter setting and not station pressure. Null when GFS does not cover the window."
        },
        "visibility_min": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "cloud_ceiling_min": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "thunderstorm_probability_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "aqi_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "feels_like_high": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "feels_like_low": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "solar_radiation_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Peak downward shortwave radiation for the row (W/m^2)."
        },
        "snow_level": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Mean snow level above mean sea level (unit per `units.altitude`); `0` is a real value. Null only when no forecast source covers the row."
        },
        "thunder_possible": {
          "type": "boolean",
          "default": false,
          "description": "Distilled flag; true when the row crosses the blender's lightning-risk threshold."
        },
        "astro": {
          "$ref": "#/$defs/Astro"
        },
        "day": {
          "$ref": "#/$defs/DailyPeriod"
        },
        "night": {
          "$ref": "#/$defs/DailyPeriod"
        },
        "threats": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/DailyThreat"
          },
          "description": "Per-day hazard callouts (winter, severe, heat, etc.)."
        },
        "relative_day": {
          "type": "integer",
          "description": "Signed calendar offset from the local current date at request time\n(-1 = yesterday, 0 = today, 1 = tomorrow). Before 6 AM local an\nextra entry with `relative_day: -1` prepends the in-progress\novernight row. That row always reports `night` and never `day`,\nits daytime being past; `low` whenever a source carries it (see\n`low`); `high`, when present, is that date's observed or forecast\nmaximum (see `high`). Select today by `relative_day == 0` rather\nthan by\nposition in the array, which shifts by one inside that window.\n"
        },
        "active_period": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "day",
            "night"
          ],
          "description": "Non-null on exactly the row containing the current local time\n(`day` during 06:00–17:59, `night` during 18:00–05:59). Clients\nuse this with `relative_day` to label rows Today/Tonight without\nwall-clock heuristics.\n"
        },
        "uncertainty": {
          "x-stability": "experimental",
          "description": "Optional NBM probabilistic sub-object for this day. Populated only\nwhen `include=uncertainty` is requested and NBM coverage applies.\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n",
          "oneOf": [
            {
              "$ref": "#/$defs/DailyUncertainty"
            },
            {
              "type": "null"
            }
          ]
        },
        "climate": {
          "description": "Optional climate sub-object for this day. Populated only when\n`include=climate` is requested and the nearest station holds a\nnormal for the date.\n",
          "oneOf": [
            {
              "$ref": "#/$defs/DailyClimate"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "DailyClimate": {
      "type": "object",
      "description": "The day's normal paired with this forecast's departure from it.\n\nTemperatures are in the requested `unit` system, matching `high` and\n`low` on the same row. Departures are signed so that positive is\nwarmer than normal, and are differences rather than absolute\ntemperatures.\n\nEvery field is nullable: a station can hold a normal high without a\nnormal low, and a forecast day can fall in a gap in the station's\nrecord. A `0` departure means the forecast landed exactly on the\nnormal, which is not the same as an absent one.\n\nStation provenance for these values — which station, how far away,\nand over what base period — lives in the top-level `climate` block.\n",
      "properties": {
        "normal_high": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Normal daily maximum temperature for this day of year."
        },
        "normal_low": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Normal daily minimum temperature for this day of year."
        },
        "high_departure": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Forecast high minus normal high; positive is warmer than normal."
        },
        "low_departure": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Forecast low minus normal low; positive is warmer than normal."
        }
      }
    },
    "DailyPeriod": {
      "type": "object",
      "description": "Forecast data for a single period (day or night) within a\n`Daily` entry.\n",
      "properties": {
        "temperature": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "High for `day`, low for `night`; null when the half has no temperature."
        },
        "wind_speed": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Sustained wind speed for the half; `0` is calm (below 1 knot, FMH-1 5.5.2: served as `0` in every unit system). Null only when no forecast source covers the half."
        },
        "wind_direction": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 360,
          "description": "Wind direction in degrees (0 = N). Null when calm (`wind_speed` 0) and when no forecast source covers the half."
        },
        "precip_probability": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability of measurable precipitation (0–100) for the day or night period. When `include=nowcast` is requested, the active near-term period may be raised or lowered from hourly values reconciled against the radar nowcast (NWS longer-period ≥ subperiod consistency). `0` is a dry period. Null only when no forecast source covers the half.\n"
        },
        "precip_total": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Null when the row's `precip_total` is null."
        },
        "snow_total": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Null when the row's `snow_total` is null."
        },
        "ice_total": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Null when the row's `ice_total` is null."
        },
        "humidity": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Relative humidity, percent. Null only when no forecast source covers the half."
        },
        "cloud_cover": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Cloud cover, percent; `0` is clear. Null only when no forecast source covers the half."
        },
        "wind_gust": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Maximum gust for the half, served only next to a non-calm `wind_speed`. Null when no source carries a gust, or the wind is calm or unknown."
        },
        "dew_point": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "feels_like": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "High-for-day / low-for-night of the hourly feels-like temperature (see `Current.feels_like`)."
        },
        "visibility": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "pressure": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Mean of the half's hourly sea-level pressure (see `Hourly.pressure`): GFS PRMSL, except that a half holding the Now hour includes that hour's current observation when `include=current` resolves it."
        },
        "cloud_ceiling": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "uv_index": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Max UV index within the period (day only; ~0 overnight)."
        },
        "thunderstorm_probability": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "cape": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Maximum convective available potential energy (J/kg). Null only when no forecast source covers the half."
        },
        "aqi": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "precip_type": {
          "type": "string",
          "enum": [
            "",
            "none",
            "rain",
            "snow",
            "mix",
            "sleet",
            "freezing_rain",
            "unknown"
          ],
          "description": "Precipitation type for the period: the precipitation its\n`conditions` and `narrative` report, read from the hours ending\nwithin the period (06:00 to 18:00 local for `day`, 18:00 to\n06:00 for `night`). Where daily forecast products cover the\nlocation, every such hour counts, within `hourly_hours` or past\nit; otherwise only the hours within `hourly_hours` do. `hourly`\nneed not be included. Omitted when the period reports no\nprecipitation (PoP under 15, or no type covering enough of its\nhours) and for a period none of those hours reach. `mix` is\nrain and snow, or a wintry mix; `sleet` is ice pellets. `\"\"`,\n`none` and `unknown` are reserved and not currently served.\nThunder is never a type: it is reported in `conditions` and\n`thunderstorm_probability` (`thunder_possible` flags thunder\nwith snow).\n"
        },
        "thunder_possible": {
          "type": "boolean",
          "default": false
        },
        "conditions": {
          "type": "string"
        },
        "icon": {
          "type": "string"
        },
        "severe": {
          "type": "boolean",
          "default": false
        },
        "narrative": {
          "type": "string",
          "description": "Human-readable narrative for this period."
        }
      }
    },
    "DailyThreat": {
      "type": "object",
      "description": "Per-day hazard callout.",
      "required": [
        "type",
        "risk_label"
      ],
      "properties": {
        "type": {
          "type": "string",
          "description": "Hazard category (e.g., `severe`, `winter`, `heat`, `fire`)."
        },
        "risk": {
          "type": "string",
          "description": "Raw risk identifier from the upstream source."
        },
        "probability": {
          "type": "number",
          "format": "double",
          "minimum": 0,
          "maximum": 1,
          "description": "Fractional probability (0-1) when known."
        },
        "risk_label": {
          "type": "string",
          "description": "Human-readable risk label (e.g., `\"Slight\"`, `\"Enhanced\"`)."
        }
      }
    },
    "DailyUncertainty": {
      "type": "object",
      "x-stability": "experimental",
      "description": "**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n\nNBM probabilistic companion fields for a single day, from the NBM\ndaily products. All fields are optional. The daily rules store these\nunder the same `standard_name`s as the hourly fields; each property\nnames the daily product it is read from. NBM stores daily rows\nstamped 00Z and 12Z UTC on each date; a row's fields aggregate the\nrows stamped with its `date` (the UTC date, as for every daily field),\n`*_avg` fields as their mean and `*_max` fields as their maximum.\n",
      "properties": {
        "temperature_stddev_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Day-mean ensemble standard deviation of temperature, in the\nrequested temperature unit (offset-free).\nCatalog: `temperature_2m_stddev_mean` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.\n"
        },
        "dew_point_stddev_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Day-mean ensemble standard deviation of dew point, in the\nrequested temperature unit (offset-free).\nCatalog: `dew_point_2m_stddev_mean` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.\n"
        },
        "wind_speed_stddev_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Day-mean ensemble standard deviation of wind speed, in the\nrequested wind speed unit.\nCatalog: `wind_speed_10m_stddev_mean` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.\n"
        },
        "wind_gust_stddev_avg": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Day-mean ensemble standard deviation of wind gust, in the\nrequested wind speed unit.\nCatalog: `wind_gusts_10m_stddev_mean` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.\n"
        },
        "temperature_max_stddev": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Ensemble spread around `high`: NBM's standard deviation of the\ndaily maximum temperature, in the requested temperature unit\n(offset-free). Present only when `high` is NBM's native daily\nmaximum, read from the same stored row; absent when `high` comes\nfrom another source or was adjusted.\nCatalog: `temperature_2m_max_stddev` on `nbm_daily_temps` / `nbm_daily_temps_ak` / `nbm_daily_temps_hi`.\n"
        },
        "temperature_min_stddev": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Ensemble spread around `low`: NBM's standard deviation of the\ndaily minimum temperature, in the requested temperature unit\n(offset-free). Present only when `low` is NBM's native daily\nminimum, read from the same stored row (the following morning's\nminimum when that one sets `low`); absent when `low` comes from\nanother source or was adjusted.\nCatalog: `temperature_2m_min_stddev` on `nbm_daily_temps` / `nbm_daily_temps_ak` / `nbm_daily_temps_hi`.\n"
        },
        "visibility_prob_below_1mi_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Day-maximum probability (percent) that visibility is below 1 mi.\nCatalog: `visibility_prob_below_1mi_max` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.\n"
        },
        "ceiling_prob_below_1000ft_max": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Day-maximum probability (percent) that the cloud ceiling is below\n1000 ft AGL.\nCatalog: `ceiling_prob_below_1000ft_max` on `nbm_daily_surface` / `nbm_daily_surface_ak` / `nbm_daily_surface_hi`.\n"
        }
      }
    },
    "Discussions": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Deprecated. Prefer `include=guidance` (`guidance.mcds` /\n`guidance.afd`). Map polygons live on `GET /v1/guidance/mesoscale`.\n\n**Experimental.** Field shapes may change without notice: excluded\nfrom the 90-day breaking-change commitment.\n",
      "required": [
        "mesoscale"
      ],
      "properties": {
        "mesoscale": {
          "type": "array",
          "description": "Active SPC Mesoscale Discussions whose polygons overlap the\nlocation. Expired products are omitted. Cap 10 (newest first).\n",
          "items": {
            "$ref": "#/$defs/MCDGuidance"
          }
        },
        "area_forecast": {
          "allOf": [
            {
              "$ref": "#/$defs/AFDGuidance"
            }
          ],
          "description": "Latest Area Forecast Discussion for the WFO covering the point.\nOmitted when no AFD is available.\n"
        }
      }
    },
    "GIS": {
      "type": "object",
      "description": "Geographic metadata for the resolved location. Contains coordinate\ndata, political boundaries, population, timezone, and nearest\nNEXRAD radar site metadata.\n\nRadar data URLs (visual-products manifest, NEXRAD imagery) are\n**not** returned in the v1 response. Radar imagery will ship later\nas a separate `/v1/radar/*` family.\n",
      "required": [
        "lat",
        "lon",
        "timezone"
      ],
      "properties": {
        "lat": {
          "type": "number",
          "format": "double",
          "minimum": -90,
          "maximum": 90
        },
        "lon": {
          "type": "number",
          "format": "double",
          "minimum": -180,
          "maximum": 180
        },
        "city": {
          "type": "string",
          "description": "City name covering the point (if the point lies inside city limits)."
        },
        "nearest_city": {
          "type": "string",
          "description": "Nearest named city (useful when `city` is not populated)."
        },
        "state": {
          "type": "string",
          "description": "State or equivalent subdivision (abbreviation)."
        },
        "county": {
          "type": "string"
        },
        "country": {
          "type": "string",
          "description": "ISO 3166-1 alpha-2 country code."
        },
        "population": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "description": "Approximate population of `city` or `nearest_city`."
        },
        "timezone": {
          "type": "string",
          "description": "IANA timezone identifier."
        },
        "nearest_radar": {
          "$ref": "#/$defs/NearestRadar"
        },
        "nearest_station": {
          "$ref": "#/$defs/NearestStation"
        }
      }
    },
    "Guidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "NWS forecaster text products (AFD, HWO, NOW, HLS, MCD, MPD, PMD) for the point:\nstructured fields plus raw bulletin `body` when available. Canonical\nweather surface for forecaster guidance; map polygons live on\n`GET /v1/guidance/mesoscale` and `GET /v1/guidance/precipitation`\n(bodies omitted there). `guidance.now` is the WFO short-term NOW\nproduct, not the radar `nowcast` block. `guidance.hls` is the WFO\nHurricane Local Statement for the office covering the point; omitted\nwhen none is live. `guidance.pmds` is the latest national Prognostic\nDiscussion per enabled desk (SPD/EPD/HI/CA/MRD/THR/ENS); omitted when empty.\nDistinct from `guidance.mpds` (WPC precipitation discussions).\n\n**Experimental.** Field shapes may change without notice: excluded\nfrom the 90-day breaking-change commitment.\n",
      "required": [
        "active_mcds"
      ],
      "properties": {
        "afd": {
          "$ref": "#/$defs/AFDGuidance"
        },
        "hwo": {
          "$ref": "#/$defs/HWOGuidance"
        },
        "now": {
          "$ref": "#/$defs/NOWGuidance"
        },
        "hls": {
          "$ref": "#/$defs/HLSGuidance"
        },
        "active_mcds": {
          "type": "integer",
          "minimum": 0,
          "description": "Count of non-expired SPC Mesoscale Discussions whose polygons\noverlap the location (matches `mcds` length when present).\n"
        },
        "mcds": {
          "type": "array",
          "description": "SPC Mesoscale Discussions overlapping the location (structured\nfields plus raw `body`). Expired products are omitted. Cap 10\n(newest first).\n",
          "items": {
            "$ref": "#/$defs/MCDGuidance"
          }
        },
        "active_mpds": {
          "type": "integer",
          "minimum": 0,
          "description": "Count of non-expired WPC Mesoscale Precipitation Discussions\nwhose polygons overlap the location (matches `mpds` length when\npresent). Always emitted; not in `required` so older clients\nkeep decoding.\n"
        },
        "mpds": {
          "type": "array",
          "description": "WPC Mesoscale Precipitation Discussions overlapping the location.\nExpired products are omitted. Cap 10 (newest first).\n",
          "items": {
            "$ref": "#/$defs/MPDGuidance"
          }
        },
        "pmds": {
          "type": "array",
          "description": "Latest national Prognostic Discussion per enabled desk: WPC\n(`PMDSPD`, `PMDEPD`, `PMDHI`, `PMDCA`) and CPC\n(`PMDMRD` 6-10/8-14 day, `PMDTHR` week-2 hazards, `PMDENS`\nENSO). Omitted when\nempty. Distinct from `mpds` (precipitation discussions).\nEach desk is fetched by `awips_id` (limit 1) and dropped\nwhen older than its cadence allows (3 days; `PMDCA` and\n`PMDTHR` 7, `PMDENS` 45).\n",
          "items": {
            "$ref": "#/$defs/PMDGuidance"
          }
        },
        "swo_day1_risk": {
          "type": "string",
          "description": "SPC Day-1 convective outlook categorical risk label (when active)."
        }
      }
    },
    "HLSGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "WFO Hurricane Local Statement for the office covering the point:\npipeline LLM `summary` plus optional raw bulletin `body`. Latest\nproduct for the WFO (`office_for_point`); omitted when none is live.\nNo map overlay and no tropical-storm join.\n",
      "properties": {
        "summary": {
          "type": "string",
          "description": "Pipeline LLM summary when enrichment has run."
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "wfo": {
          "type": "string",
          "description": "Issuing Weather Forecast Office identifier."
        },
        "body": {
          "type": "string",
          "description": "Full HLS bulletin text when available."
        }
      }
    },
    "HWOGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Hazardous Weather Outlook: structured fields plus optional raw bulletin\n`body`.\n",
      "required": [
        "has_active_hazards"
      ],
      "properties": {
        "day_one": {
          "type": "string",
          "description": "Day-one hazard summary text."
        },
        "summary": {
          "type": "string",
          "description": "Pipeline LLM overall hazard summary when enrichment has run."
        },
        "has_active_hazards": {
          "type": "boolean"
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "body": {
          "type": "string",
          "description": "Full HWO bulletin text when available."
        }
      }
    },
    "Hourly": {
      "type": "object",
      "description": "Single hourly forecast point.\n\nThe Now hour (the first row, when its `time` is in the current\nhour) carries the current observation where `include=current`\nresolves it, so it matches `current`: `temperature`, and each of\n`feels_like`, `humidity`, `dew_point`, `wind_speed` with\n`wind_direction`, `wind_gust`, `pressure`, `visibility`,\n`cloud_ceiling`, `cloud_cover`, `aqi`, `uv_index`, `conditions`\nwith `precip_type`, and `icon` that the current observation\nresolved. A field it did not resolve keeps the forecast's value.\nLater hours are the forecast.\n",
      "required": [
        "time"
      ],
      "properties": {
        "time": {
          "type": "string",
          "format": "date-time",
          "description": "Forecast valid time (ISO 8601, UTC)."
        },
        "temperature": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "feels_like": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "humidity": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "wind_speed": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Sustained wind speed; `0` is calm (below 1 knot, FMH-1 5.5.2: served as `0` in every unit system). Null only when no forecast source covers the hour."
        },
        "wind_direction": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 360,
          "description": "Wind direction in degrees (0 = N). Null when calm (`wind_speed` 0) and when no forecast source covers the hour."
        },
        "precip_probability": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability of measurable precipitation (0–100) for the hour. When `include=nowcast` is requested, near-term hours overlapping the radar nowcast horizon are reconciled against the nowcast summary: wet peak intensity may floor values upward; when peak intensity is `none`, covered hours may be pulled down toward zero (unless the hour already carries precip conditions). Null when the hour's amount is no data and no model PoP covers the hour.\n"
        },
        "precip_amount": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Liquid-equivalent precipitation for the hour ending at `time`. `0` is a dry hour; null when no forecast source covers the hour."
        },
        "conditions": {
          "type": "string"
        },
        "icon": {
          "type": "string"
        },
        "severe": {
          "type": "boolean",
          "default": false
        },
        "wind_gust": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Wind gust, served only next to a non-calm `wind_speed`. Null when no source carries a gust, or the wind is calm or unknown."
        },
        "dew_point": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "cloud_cover": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "visibility": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Visibility distance. Null past the blend's visibility horizon, about 3 days."
        },
        "pressure": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Sea-level pressure (unit per `units.pressure`): the quantity US NWS stations report in the METAR SLP group, from the GFS mean-sea-level pressure field at the row's time. On the Now hour with `include=current` it is the current observation's value (`Current.pressure`, the reduced RTMA analysis) when that resolved. Not the altimeter setting and not station pressure."
        },
        "thunderstorm_probability": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "uv_index": {
          "type": [
            "number",
            "null"
          ],
          "format": "double"
        },
        "aqi": {
          "type": [
            "number",
            "null"
          ]
        },
        "precip_type": {
          "type": "string",
          "enum": [
            "none",
            "rain",
            "snow",
            "mix",
            "sleet",
            "freezing_rain",
            "hail",
            "unknown"
          ],
          "description": "Precipitation type for the hour. Omitted when the hour has no\ntype, or has neither a precipitation chance nor an amount.\n`mix` is rain and snow, or a wintry mix; `sleet` is ice pellets.\n`none`, `hail` and `unknown` are reserved and not currently\nserved. Thunder is never a type: it is reported in `conditions`\nand `thunderstorm_probability` (`thunder_possible` flags thunder\nwith snow).\n"
        },
        "precip_type_rain_prob": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "precip_type_snow_prob": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "precip_type_freezing_rain_prob": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "precip_type_ice_pellets_prob": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100
        },
        "cape": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Convective available potential energy (J/kg)."
        },
        "snow_amount": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Snow for the hour ending at `time`. `0` on a dry hour a snow source covers; null only when no source covers the hour."
        },
        "cloud_ceiling": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "description": "NBM forecast cloud-base height above ground, in the `units.altitude` unit. Null when no ceiling is reported (clear or high thin cloud)."
        },
        "cloud_cover_low": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "HRRR low-cloud-layer cover, percent (CONUS). Present only with `cloud_cover_mid` and `cloud_cover_high` for hours inside the HRRR horizon (18 h, or 48 h on 00/06/12/18Z cycles)."
        },
        "cloud_cover_mid": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "HRRR mid-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`."
        },
        "cloud_cover_high": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "HRRR high-cloud-layer cover, percent (CONUS). Same all-three-or-null contract as `cloud_cover_low`."
        },
        "snow_level": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Snow level above mean sea level (unit per `units.altitude`); `0` is a real value. Null only when no forecast source covers the hour."
        },
        "thunder_possible": {
          "type": "boolean",
          "default": false,
          "description": "True when any hourly thunder indicator (`thunderstorm_probability`\nor `cape`) crosses the blender's lightning-risk threshold for this\nhour. This is a distilled flag; use\n`uncertainty.*` for raw probabilistic fields.\n"
        },
        "uncertainty": {
          "x-stability": "experimental",
          "description": "Optional probabilistic sub-object sourced from the NBM model.\nPopulated only when the caller requests `include=uncertainty` and\nNBM coverage is available for the location (CONUS, Alaska, or\nHawaii).\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n",
          "oneOf": [
            {
              "$ref": "#/$defs/HourlyUncertainty"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "HourlyUncertainty": {
      "type": "object",
      "x-stability": "experimental",
      "description": "**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n\nNBM probabilistic companion fields for a single hour. Each field is\noptional and absent when NBM does not carry it for the hour (ceiling\nand visibility probabilities are not on every frame); the sub-object\nis omitted when no field is available. Each property names the\ncatalog `standard_name` and products it is read from, so the same\nseries can be fetched through `/v1/series`.\n",
      "properties": {
        "temperature_stddev": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Ensemble standard deviation of `temperature`, in the requested\ntemperature unit (a spread, converted without the °F/°C offset).\nCatalog: `temperature_2m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "dew_point_stddev": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Ensemble standard deviation of `dew_point`, in the requested\ntemperature unit (offset-free).\nCatalog: `dew_point_2m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "wind_speed_stddev": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Ensemble standard deviation of `wind_speed`, in the requested\nwind speed unit.\nCatalog: `wind_speed_10m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "wind_gust_stddev": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "description": "Ensemble standard deviation of `wind_gust`, in the requested\nwind speed unit.\nCatalog: `wind_gusts_10m_stddev` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "visibility_prob_below_1mi": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that visibility is below 1 mi.\nCatalog: `visibility_prob_below_1mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "visibility_prob_below_2mi": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that visibility is below 2 mi.\nCatalog: `visibility_prob_below_2mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "visibility_prob_below_3mi": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that visibility is below 3 mi.\nCatalog: `visibility_prob_below_3mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "visibility_prob_below_5mi": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that visibility is below 5 mi.\nCatalog: `visibility_prob_below_5mi` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "ceiling_prob_below_500ft": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that the cloud ceiling is below 500 ft AGL.\nCatalog: `ceiling_prob_below_500ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "ceiling_prob_below_1000ft": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that the cloud ceiling is below 1000 ft AGL.\nCatalog: `ceiling_prob_below_1000ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "ceiling_prob_below_2000ft": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that the cloud ceiling is below 2000 ft AGL.\nCatalog: `ceiling_prob_below_2000ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "ceiling_prob_below_3000ft": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that the cloud ceiling is below 3000 ft AGL.\nCatalog: `ceiling_prob_below_3000ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        },
        "ceiling_prob_below_6600ft": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 100,
          "description": "Probability (percent) that the cloud ceiling is below 6600 ft AGL.\nCatalog: `ceiling_prob_below_6600ft` on `nbm_conus` / `nbm_alaska` / `nbm_hawaii`.\n"
        }
      }
    },
    "Location": {
      "type": "object",
      "description": "Resolved location.",
      "required": [
        "lat",
        "lon",
        "name",
        "timezone"
      ],
      "properties": {
        "lat": {
          "type": "number",
          "format": "double",
          "minimum": -90,
          "maximum": 90
        },
        "lon": {
          "type": "number",
          "format": "double",
          "minimum": -180,
          "maximum": 180
        },
        "name": {
          "type": "string",
          "description": "Human-readable place name, usually without the state; see\n`region`. The resolved place name, else the city, else the\nnearest city. Empty when none is known.\n"
        },
        "region": {
          "type": "string",
          "description": "State or region abbreviation."
        },
        "country": {
          "type": "string",
          "description": "ISO 3166-1 alpha-2 country code."
        },
        "timezone": {
          "type": "string",
          "description": "IANA timezone identifier."
        },
        "county": {
          "type": "string",
          "description": "County name (US only)."
        },
        "zip": {
          "type": "string",
          "description": "ZIP code (US only)."
        }
      }
    },
    "MCDGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "SPC Mesoscale Discussion overlapping the point: distilled properties\nplus optional raw bulletin `body`.\n",
      "properties": {
        "mcd_number": {
          "type": "integer",
          "description": "SPC mesoscale discussion number."
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "expires_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "End of the product valid window when parsed."
        },
        "concerning": {
          "type": "string",
          "description": "SPC \"Concerning...\" line (e.g. severe potential / watch possible)."
        },
        "areas_affected": {
          "type": "string"
        },
        "watch_probability": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100,
          "description": "Stated probability of watch issuance, percent."
        },
        "summary": {
          "type": "string",
          "description": "Pipeline LLM summary when enrichment has run."
        },
        "body": {
          "type": "string",
          "description": "Full MCD bulletin text when available."
        }
      }
    },
    "MPDGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "WPC Mesoscale Precipitation Discussion overlapping the point:\ndistilled properties plus optional raw bulletin `body`.\n",
      "properties": {
        "mpd_number": {
          "type": "integer",
          "description": "WPC mesoscale precipitation discussion number."
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "expires_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "concerning": {
          "type": "string"
        },
        "areas_affected": {
          "type": "string"
        },
        "flash_flood_tier": {
          "type": "string",
          "description": "Headline tier (possible / likely / occurring)."
        },
        "summary": {
          "type": "string"
        },
        "body": {
          "type": "string"
        }
      }
    },
    "Meta": {
      "type": "object",
      "description": "Per-response metadata: response schema version, generation time,\nupstream data freshness, and any non-fatal warnings.\n",
      "required": [
        "schema_version",
        "generated_at"
      ],
      "properties": {
        "schema_version": {
          "type": "string",
          "description": "Response schema version, a semver string (currently `1.0.0`).\nThe major part changes only with a non-additive change, which\nfollows the deprecation policy. Cache on it if you cache\nresponse shapes.\n"
        },
        "generated_at": {
          "type": "string",
          "format": "date-time",
          "description": "Server time at which this response body was generated."
        },
        "freshness": {
          "type": "array",
          "description": "Freshness metadata for each upstream data source used. It never omits a product whose data produced a forecast row or section this response serves, and may list one whose contribution is not visible: attribution is per row, so a masked value or a field projection (`fields`, `view`) does not narrow it.",
          "items": {
            "$ref": "#/$defs/SourceFreshness"
          }
        },
        "warnings": {
          "type": "array",
          "description": "Non-fatal warnings generated during request fulfillment. Each\nentry has a stable `code` clients can branch on and a\nhuman-readable `detail`.\n",
          "items": {
            "$ref": "#/$defs/MetaWarning"
          }
        },
        "hourly_sampling": {
          "type": "object",
          "description": "Present only when `hourly_step` dropped at least one hourly\nrow. Echoes the effective thinning so a thinned series is\ndistinguishable from a short forecast: every row is kept below\n`from_hour` hours from `hourly[0].time`, then one row every\n`step_hours` hours.\n",
          "required": [
            "step_hours",
            "from_hour"
          ],
          "properties": {
            "step_hours": {
              "type": "integer",
              "minimum": 2,
              "maximum": 24,
              "description": "Effective `hourly_step`."
            },
            "from_hour": {
              "type": "integer",
              "minimum": 0,
              "maximum": 168,
              "description": "Effective `hourly_step_from`."
            }
          }
        }
      }
    },
    "MetaWarning": {
      "type": "object",
      "required": [
        "code",
        "section"
      ],
      "properties": {
        "code": {
          "type": "string",
          "description": "Stable machine-parseable warning code. Clients should branch on\n`code`, not on `detail`.\n\n- `partial_fetch`: the section could not be fully fetched and is\n  missing or incomplete.\n- `upstream_timeout`: an upstream weather source timed out, so\n  the section is missing or incomplete.\n- `upstream_unavailable`: an upstream weather source was\n  unreachable, or (for a section not enabled for every key,\n  such as `sky`) access could not be checked, so the section\n  is missing or incomplete; retry later.\n- `coverage_limited`: the data does not cover this location\n  (for example no nearby station, or a product outside its\n  domain); retrying will not help.\n- `geocode_ambiguous`: the place name matched more than one\n  location; the response uses the best match.\n- `stale_response`: the section was served from a recent cached\n  copy because the upstream source failed.\n- `stale_source`: one upstream product's latest data is older\n  than its source's freshness limit. `product_id` names the\n  product (its age is the `meta.freshness` entry with the same\n  `product_id`) and `fields` lists the fields in `section` it\n  leaves `null` (or omits, for optional fields such as\n  `aqi_category`). Unlike `stale_response`, the response was not\n  replayed from cache.\n- `not_enabled`: the requested section is not enabled for this\n  API key.\n\nNew codes may be added without a breaking-change announcement;\ntreat an unknown code as informational.\n",
          "enum": [
            "partial_fetch",
            "upstream_timeout",
            "upstream_unavailable",
            "coverage_limited",
            "geocode_ambiguous",
            "stale_response",
            "stale_source",
            "not_enabled"
          ]
        },
        "section": {
          "type": "string",
          "description": "Response section the warning applies to (e.g., `\"alerts\"`,\n`\"hourly\"`, `\"outlooks\"`). May also be `\"_request\"` for\nrequest-level warnings.\n"
        },
        "detail": {
          "type": "string",
          "description": "Human-readable explanation."
        },
        "product_id": {
          "type": "string",
          "description": "Catalog product id (`GET /v1/catalog/{product_id}`) of the\nupstream product the warning is about; the same id as the\nproduct's `meta.freshness` entry. Set only for `stale_source`.\n"
        },
        "fields": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Response fields in `section` that this warning leaves `null` or\nomits. Set only for `stale_source`.\n"
        }
      }
    },
    "Metar": {
      "type": "object",
      "description": "Compact nearest METAR observation for the queried point. Populated\nwhen `include=metar` is requested. Sky layers and present-weather\ntokens are parsed from `raw_metar` (stop at RMK); the raw string\nitself is not forwarded.\n\nOmitted when no recent station is near the point or the fetch\nfails; those cases report `coverage_limited` or a fetch warning\nin `meta.warnings[]` rather than failing the request.\n",
      "required": [
        "station_id",
        "distance_km",
        "observed_at",
        "age_min"
      ],
      "properties": {
        "station_id": {
          "type": "string"
        },
        "distance_km": {
          "type": "number",
          "description": "Great-circle distance from the requested point to the station, rounded to 0.1 km."
        },
        "observed_at": {
          "type": "string",
          "format": "date-time"
        },
        "age_min": {
          "type": "number",
          "description": "Minutes between `observed_at` and response `meta.generated_at`."
        },
        "ceiling_ft": {
          "type": "integer",
          "description": "Lowest BKN/OVC/VV base, feet AGL."
        },
        "layers": {
          "type": "array",
          "description": "Parsed sky groups. Empty when the station reports CLR/SKC/NSC.\nOmitted when sky condition is unknown.\n",
          "items": {
            "$ref": "#/$defs/MetarSkyLayer"
          }
        },
        "weather": {
          "type": "array",
          "description": "Raw present-weather tokens (`-RA`, `VCTS`, `TSRA`).",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "MetarSkyLayer": {
      "type": "object",
      "required": [
        "cover"
      ],
      "properties": {
        "cover": {
          "type": "string",
          "enum": [
            "FEW",
            "SCT",
            "BKN",
            "OVC",
            "VV"
          ]
        },
        "base_ft": {
          "type": "integer",
          "description": "Layer base in feet AGL. Omitted when the group has no height (`VV///`)."
        },
        "convective": {
          "type": "string",
          "enum": [
            "CB",
            "TCU"
          ]
        }
      }
    },
    "NOWGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "WFO short-term NOW product (NWS NOW, not radar nowcast): pipeline\nLLM `summary` plus optional raw bulletin `body`. No `key_takeaway`\n(NOW uses the default enricher prompt).\n",
      "properties": {
        "summary": {
          "type": "string",
          "description": "Pipeline LLM summary when enrichment has run."
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "wfo": {
          "type": "string",
          "description": "Issuing Weather Forecast Office identifier."
        },
        "body": {
          "type": "string",
          "description": "Full NOW bulletin text when available."
        }
      }
    },
    "Narrations": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Rule-based narrative text for current and short-term conditions,\ngenerated deterministically from the blended forecast (not an LLM).\n\n**Experimental.** Field shapes and prose styling may change without\nnotice: excluded from the 90-day breaking-change commitment.\n",
      "properties": {
        "current": {
          "type": "string",
          "description": "Time-of-day-aware summary of current conditions. Weaves in, when\navailable, a category- and time-aware alert cue (e.g. \"Elevated\nfire-weather conditions through 1 AM.\"), imminent-precip nowcast, an\noptional plain-language cue from an overlapping SPC Mesoscale\nDiscussion (truncated enrichment summary; not a second LLM call),\nthe current observation, the next-few-hours trend, and a time-aware\nhigh/low (today's high during the day; tonight's low and tomorrow's\nhigh in the evening). Phrasing varies by location and hour for natural\nvariety but is stable between refreshes within the hour. Temperatures\nfollow the requested `units`. Requires `current` data; `daily` adds\nthe high/low, `alerts` the alert cue, `nowcast` the imminent-precip\ncue, and overlapping mesoscale discussions the optional guidance cue.\nArea Forecast Discussions and Hazardous Weather Outlooks are not\nfolded into this field.\n"
        },
        "short_term": {
          "type": "string",
          "description": "Narrative covering the next few hours."
        }
      }
    },
    "NearestRadar": {
      "type": "object",
      "description": "Metadata for the nearest NEXRAD radar site. Data URLs are not\nincluded here; a later `/v1/radar/*` family will return them.\n",
      "required": [
        "id",
        "name",
        "lat",
        "lon",
        "distance_km"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Radar site code without ICAO prefix (e.g., `LOT`, `OKX`, `AKC`)."
        },
        "name": {
          "type": "string"
        },
        "lat": {
          "type": "number",
          "format": "double",
          "minimum": -90,
          "maximum": 90
        },
        "lon": {
          "type": "number",
          "format": "double",
          "minimum": -180,
          "maximum": 180
        },
        "distance_km": {
          "type": "number",
          "format": "double",
          "minimum": 0,
          "description": "Great-circle distance from the requested point to the radar site, rounded to 0.1 km."
        }
      }
    },
    "NearestStation": {
      "type": "object",
      "description": "Metadata for the nearest METAR observation station.",
      "required": [
        "id",
        "lat",
        "lon",
        "distance_km"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "ICAO station identifier (e.g., `KJFK`)."
        },
        "name": {
          "type": "string"
        },
        "state": {
          "type": "string"
        },
        "lat": {
          "type": "number",
          "format": "double"
        },
        "lon": {
          "type": "number",
          "format": "double"
        },
        "distance_km": {
          "type": "number",
          "format": "double",
          "minimum": 0,
          "description": "Great-circle distance from the requested point to the station, rounded to 0.1 km."
        },
        "elevation_m": {
          "type": [
            "number",
            "null"
          ]
        }
      }
    },
    "Nowcast": {
      "type": "object",
      "description": "Minute-scale MRMS radar precipitation nowcast for the queried\nlocation. Populated when `include=nowcast` is requested.\n",
      "properties": {
        "interval_seconds": {
          "type": "integer",
          "description": "Length of each step, in seconds: the most common spacing\nbetween the frame's lead times (300 today).\n"
        },
        "generated_at": {
          "type": "string",
          "format": "date-time",
          "description": "Nowcast reference (anchor) time from the latest radar frame."
        },
        "summary": {
          "$ref": "#/$defs/NowcastSummary"
        },
        "points": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/NowcastPoint"
          }
        }
      }
    },
    "NowcastPoint": {
      "type": "object",
      "required": [
        "time",
        "type",
        "intensity_dbz",
        "intensity"
      ],
      "properties": {
        "time": {
          "type": "string",
          "format": "date-time"
        },
        "type": {
          "type": "string",
          "description": "Radar precipitation type at this step: `none`, `rain`, `snow`,\n`freezing_rain`, `wintry_mix`, `no_coverage` (outside radar\ncoverage), or `unknown` for an unrecognized radar type code.\n"
        },
        "intensity_dbz": {
          "type": "number",
          "format": "double"
        },
        "intensity": {
          "type": "string",
          "enum": [
            "none",
            "light",
            "moderate",
            "heavy"
          ]
        }
      }
    },
    "NowcastSummary": {
      "type": "object",
      "description": "Server-computed headline for the first precipitation episode in the\nnowcast window, derived from the frame's steps. Each step covers one\n`interval_seconds` interval starting at its `time`. The frame's\nfirst lead is `generated_at` + 5 minutes today, and the window runs\nto the last lead's `time` + one interval.\n\nA step is wet when its `intensity` is not `none`. Precipitation\nstarts at the first of two consecutive wet steps; a lone wet step\nis not an onset. The episode ends at the first two consecutive dry\nsteps after the onset (one dry step inside the episode does not\nend it).\n\nA lead the radar frame has no value for is a missing step: it is\nleft out of `points` but keeps its place in the window and counts\nas dry. So wet points either side of a missing step are not\nconsecutive, and missing steps count toward the two dry steps that\nend an episode, including missing leads at the end of the window.\nWhen leading leads are missing, `points[0]` is later than\n`generated_at` + 5 minutes.\n\nWith no onset, the summary is `type: none`, `peak_intensity: none`,\nand `starts_at`, `ends_at`, `duration_minutes` and\n`already_occurring` are omitted.\n\nThe summary is computed from the latest nowcast frame and the\nrequest time; `generated_at` says how old that frame is.\n",
      "properties": {
        "starts_at": {
          "type": "string",
          "format": "date-time",
          "description": "`time` of the onset step: the first of two consecutive wet\nsteps. Omitted when there is no onset.\n"
        },
        "ends_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "End of the last wet step of the episode: that step's `time`\nplus `interval_seconds`. Omitted while precipitation continues\nto the end of the window (no two consecutive dry or missing\nsteps after the onset), and when there is no onset.\n"
        },
        "duration_minutes": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Minutes from `starts_at` to `ends_at`, so a multiple of the step\nlength (at least 10 for a two-step episode). Omitted whenever\n`ends_at` is omitted.\n"
        },
        "type": {
          "type": "string",
          "description": "Precipitation type of the onset step, as on `points[].type`\n(`rain`, `snow`, `freezing_rain`, `wintry_mix`, or `unknown` for\nan unrecognized radar type code), or `none` when there is no\nonset.\n"
        },
        "peak_intensity": {
          "type": "string",
          "enum": [
            "none",
            "light",
            "moderate",
            "heavy"
          ],
          "description": "Highest `intensity` across the episode, from the onset to its\nend (or to the end of the window while it continues). Later\nepisodes in the window are not counted. `none` when there is no\nonset.\n"
        },
        "already_occurring": {
          "type": "boolean",
          "description": "True when `starts_at` is at or before the request time: the\nonset step has begun. A lone wet step is not an onset, so it\nnever sets this. The first lead is `generated_at` + 5 minutes,\nso a request in the first minutes after a frame sees false here\nwith `starts_at` still ahead. Omitted when false, and when\nthere is no onset.\n"
        }
      }
    },
    "Outlook": {
      "type": "object",
      "description": "Single SPC convective outlook (or equivalent product) active for\nthe location.\n",
      "required": [
        "product_id",
        "type",
        "day",
        "risk_label",
        "valid",
        "expires"
      ],
      "properties": {
        "product_id": {
          "type": "string",
          "description": "Upstream product identifier (e.g., `spc_otlk_day1`)."
        },
        "type": {
          "type": "string",
          "description": "Hazard category (`severe`, `tornado`, `hail`, `wind`, etc.)."
        },
        "day": {
          "type": "integer",
          "minimum": 1,
          "maximum": 8,
          "description": "Day number relative to issuance (1 = today)."
        },
        "risk": {
          "type": "string",
          "description": "Raw risk identifier from the upstream source."
        },
        "probability": {
          "type": "number",
          "format": "double",
          "minimum": 0,
          "maximum": 1,
          "description": "Fractional probability (0-1) when the source provides one."
        },
        "risk_label": {
          "type": "string",
          "description": "Human-readable risk label."
        },
        "valid": {
          "type": "string",
          "format": "date-time"
        },
        "expires": {
          "type": "string",
          "format": "date-time"
        },
        "significant": {
          "type": "boolean",
          "description": "The location falls inside SPC's hatched significant-severe\narea for this hazard — tornadoes of EF2+, hail 2 in or\nlarger, or wind gusts of 65 kt or more.\n\nSPC ships this as its own polygon, so it is reported here as\na flag on the hazard it qualifies rather than as a separate\nentry. Absent (or `false`) outside the hatched area.\n"
        }
      }
    },
    "PMDGuidance": {
      "type": "object",
      "x-stability": "experimental",
      "description": "One enabled national Prognostic Discussion desk. Latest issuance\nfor that AWIPS id. Distinct from `MPDGuidance` (precipitation).\n",
      "properties": {
        "awips_id": {
          "type": "string",
          "description": "Full AWIPS identifier (e.g. PMDSPD)."
        },
        "pmd_desk": {
          "type": "string",
          "description": "Desk suffix after the PMD prefix (SPD, EPD, HI, CA, MRD, THR, ENS)."
        },
        "issued_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time"
        },
        "summary": {
          "type": "string",
          "description": "Pipeline LLM summary when enrichment has run."
        },
        "body": {
          "type": "string",
          "description": "Full PMD bulletin text when available."
        }
      }
    },
    "Sky": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Sunrise and sunset color forecast for this date (HRRR, CONUS).\nPresent only with `include=sky` when it is enabled for the API key\nand a forecast exists for the date; an event is `null` when there is\nno forecast for it.\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n",
      "required": [
        "sunrise",
        "sunset"
      ],
      "properties": {
        "sunrise": {
          "oneOf": [
            {
              "$ref": "#/$defs/SkyEvent"
            },
            {
              "type": "null"
            }
          ]
        },
        "sunset": {
          "oneOf": [
            {
              "$ref": "#/$defs/SkyEvent"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "SkyCurve": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Score sampled at offsets from the event, in time order;\n`offsets_min[i]` pairs with `score[i]`.\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n",
      "required": [
        "offsets_min",
        "score"
      ],
      "properties": {
        "offsets_min": {
          "type": "array",
          "items": {
            "type": "integer"
          }
        },
        "score": {
          "type": "array",
          "items": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          }
        }
      }
    },
    "SkyEvent": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Color forecast of one sunrise or sunset. Offsets are signed minutes\nfrom the event (negative before it). Scores are 0-100. A nullable\nfield is `null` when the forecast has no value for it.\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n",
      "required": [
        "score",
        "label",
        "p_vivid",
        "confidence",
        "peak_at",
        "peak_offset_min",
        "window",
        "overhead",
        "horizon",
        "horizon_occluded",
        "curve",
        "drivers",
        "source"
      ],
      "properties": {
        "score": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100,
          "description": "Peak color score."
        },
        "label": {
          "type": [
            "string",
            "null"
          ],
          "description": "Band of the score: `little color` (0-19), `some color` (20-39), `good color` (40-59), `vivid` (60-79) or `exceptional` (80-100). New bands may be added."
        },
        "p_vivid": {
          "type": [
            "number",
            "null"
          ],
          "format": "double",
          "minimum": 0,
          "maximum": 1,
          "description": "Likelihood of vivid color (a score of 60 or more)."
        },
        "confidence": {
          "type": [
            "string",
            "null"
          ],
          "description": "Forecast confidence: `low`, `medium`, `medium-high` or `high`."
        },
        "peak_at": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "The astro block's own `sunrise` or `sunset` plus `peak_offset_min`; null when that time is null."
        },
        "peak_offset_min": {
          "type": "integer",
          "description": "Minutes from the event to the peak score: -30 to 45 for sunset, -45 to 30 for sunrise, in 5-minute steps."
        },
        "window": {
          "oneOf": [
            {
              "$ref": "#/$defs/SkyWindow"
            },
            {
              "type": "null"
            }
          ],
          "description": "Null when the peak score is under 40 (no good-color window)."
        },
        "overhead": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 100,
          "description": "Peak score of the sky overhead."
        },
        "horizon": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 100,
          "description": "Peak score of the sky near the horizon."
        },
        "horizon_occluded": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "True when low and mid cloud toward the sun covers most of the horizon at the event time."
        },
        "curve": {
          "$ref": "#/$defs/SkyCurve"
        },
        "drivers": {
          "type": "array",
          "maxItems": 2,
          "items": {
            "type": "string"
          },
          "description": "Main and secondary reasons for the score, most important first, for example `high_lit`, `horizon_clear`, `low_overcast`, `smoke_dim` or `precip`. Empty when none stands out. New values may be added."
        },
        "source": {
          "type": [
            "string",
            "null"
          ],
          "description": "Model source id (the product's catalog `source.id`)."
        }
      }
    },
    "SkyWindow": {
      "type": "object",
      "x-stability": "experimental",
      "description": "Span of good color around the peak, in signed minutes from the\nevent, on the same axis as `peak_offset_min`.\n\n**Experimental** (`x-stability: experimental`): excluded from the\n90-day breaking-change commitment.\n",
      "required": [
        "start_offset_min",
        "end_offset_min"
      ],
      "properties": {
        "start_offset_min": {
          "type": "integer"
        },
        "end_offset_min": {
          "type": "integer"
        }
      }
    },
    "SourceFreshness": {
      "type": "object",
      "description": "Freshness metadata for a single upstream data source that supplied data to a forecast row or section in this response.",
      "required": [
        "source",
        "product_id",
        "age_seconds"
      ],
      "properties": {
        "source": {
          "type": "string",
          "description": "Source family (e.g., `rtma`, `hrrr`, `nbm`, `mrms`)."
        },
        "product_id": {
          "type": "string",
          "description": "Specific product identifier."
        },
        "reference_time": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Model run / observation reference time."
        },
        "latest_valid": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "Latest valid time represented in the source."
        },
        "age_seconds": {
          "type": "integer",
          "minimum": 0,
          "description": "Seconds elapsed since `reference_time`, or since `latest_valid`\nwhen there is no `reference_time` (an observed product with no\nrun). An observed daily product (for example\n`rtma_daily_ag_temps_*`) stamps each day at its start, so its age\ncounts from the start of its latest observed day, not from its\nlast update.\n"
        }
      }
    },
    "Units": {
      "type": "object",
      "description": "Unit labels for the numeric fields in this response. The exact\nlabel set is determined by the `units` request parameter.\n",
      "required": [
        "temperature",
        "wind_speed",
        "pressure",
        "visibility",
        "precip_amount"
      ],
      "properties": {
        "temperature": {
          "type": "string",
          "enum": [
            "F",
            "C"
          ],
          "description": "Temperature unit."
        },
        "wind_speed": {
          "type": "string",
          "enum": [
            "mph",
            "kph"
          ]
        },
        "pressure": {
          "type": "string",
          "enum": [
            "inHg",
            "hPa"
          ]
        },
        "visibility": {
          "type": "string",
          "enum": [
            "mi",
            "km"
          ]
        },
        "precip_amount": {
          "type": "string",
          "enum": [
            "in",
            "mm"
          ]
        },
        "altitude": {
          "type": "string",
          "enum": [
            "m",
            "ft"
          ],
          "description": "Unit for altitude-dimensioned fields (snow_level, cloud_ceiling)."
        },
        "solar_radiation": {
          "type": "string",
          "enum": [
            "W/m^2"
          ],
          "description": "Unit for irradiance fields (currently always `W/m^2`)."
        }
      }
    }
  }
}