{"components":{"responses":{"BadRequest":{"description":"Invalid parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unavailable":{"description":"Forecast data not loaded yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"schemas":{"Source":{"type":"object","required":["name","attribution","license","url","use"],"properties":{"name":{"type":"string"},"attribution":{"type":"string"},"license":{"type":"string"},"url":{"type":"string"},"use":{"type":"string"}}},"Meta":{"type":"object","required":["api_version","generated_at","as_of","forecast_fetched_at","data_version","method_url","sources"],"properties":{"api_version":{"const":"v1"},"generated_at":{"type":"string","format":"date-time"},"as_of":{"type":"string","format":"date-time","description":"Top of the hour the answer is computed for."},"forecast_fetched_at":{"type":["string","null"],"format":"date-time","description":"Newest MET Norway fetch in the snapshot."},"data_version":{"type":"string"},"method_url":{"type":"string"},"sources":{"type":"array","items":{"$ref":"#/components/schemas/Source"}},"notes":{"type":"array","items":{"type":"string"}}}},"ErrorResponse":{"type":"object","required":["error","meta"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"enum":["invalid_parameter","unavailable","not_found","method_not_allowed"]},"param":{"type":"string"},"message":{"type":"string"}}},"meta":{"$ref":"#/components/schemas/Meta"}}},"Verdict":{"type":"string","enum":["go","marginal","unknown","no"],"description":"go: it fits. marginal: it fits with a catch the why names. unknown: the hourly forecast, about two and a half days ahead, does not reach that far yet. no: it does not fit."},"Sky":{"type":"object","required":["sunrise","sunset","moon_illumination"],"properties":{"sunrise":{"type":["string","null"],"format":"date-time","description":"Sunrise at the point; null in polar day or night."},"sunset":{"type":["string","null"],"format":"date-time"},"moon_illumination":{"type":"number","minimum":0,"maximum":1,"description":"The moon's lit fraction at local midnight after the evening."}}},"SlotHour":{"type":"object","description":"One hour of a slot with its weather; fields the forecast leaves out are omitted. The one bad hour a slot may hold has good false and fails. While the forecast data holds good hours only, a bad hour that failed the default test has no weather and no fails.","required":["time","good"],"properties":{"time":{"type":"string","format":"date-time"},"good":{"type":"boolean"},"fails":{"type":"array","items":{"type":"string","enum":["rain","wind","gust","rain_chance","cold"]},"description":"What the slot's one bad hour failed."},"rain_mm":{"type":"number"},"rain_max_mm":{"type":"number","description":"The forecast's upper rain amount for the hour."},"rain_chance_pct":{"type":"number"},"thunder_chance_pct":{"type":"number"},"wind_kmh":{"type":"number"},"gust_kmh":{"type":"number"},"temp_c":{"type":"number"},"cloud_pct":{"type":"number"},"low_cloud_pct":{"type":"number"},"fog_pct":{"type":"number"}}},"Slot":{"type":"object","description":"The day's best good-weather slot at the walk's start: daylight hours with at most 2 mm of rain and wind at most 80 km/h (or the caller's tighter limits), one bad hour tolerated.","required":["start","end","good_hours","max_wind_kmh","total_precip_mm","max_rain_mm_h","wet_hours","min_temp_c","max_temp_c","max_gust_kmh","max_rain_chance_pct","max_thunder_chance_pct"],"properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"},"good_hours":{"type":"integer"},"max_wind_kmh":{"type":"number"},"total_precip_mm":{"type":"number"},"max_rain_mm_h":{"type":"number","description":"The wettest good hour's rain, in mm."},"wet_hours":{"type":"integer","description":"Good hours with at least 0.1 mm of rain."},"min_temp_c":{"type":"number"},"max_temp_c":{"type":"number"},"max_gust_kmh":{"type":["number","null"],"description":"Strongest gust across the good hours; null where the forecast data does not carry gusts."},"max_rain_chance_pct":{"type":["number","null"]},"max_thunder_chance_pct":{"type":["number","null"]},"hours":{"type":"array","items":{"$ref":"#/components/schemas/SlotHour"},"description":"The slot hour by hour, for the checked walk in a check; plan and the alternatives a check offers leave it out."}}},"Walk":{"type":"object","description":"One Walkhighlands walk on one day.","required":["walk_id","name","url","start_lat","start_lon","length_km","ascent_m","duration_h","verdict","why","start_from","start_by","slot"],"properties":{"walk_id":{"type":"string","description":"Pass to check as walk_id."},"name":{"type":"string"},"url":{"type":"string","description":"The walk's Walkhighlands page, which has the route."},"start_lat":{"type":"number"},"start_lon":{"type":"number"},"length_km":{"type":"number"},"ascent_m":{"type":"integer"},"duration_h":{"type":"number","description":"Walkhighlands' estimated time."},"from_point_km":{"type":"number","description":"From the asked point; absent for a walk checked by name."},"verdict":{"$ref":"#/components/schemas/Verdict"},"why":{"type":"string"},"start_from":{"type":["string","null"],"format":"date-time","description":"Earliest start that keeps the walk in the slot; null unless it fits."},"start_by":{"type":["string","null"],"format":"date-time","description":"Latest start that finishes inside the slot; null unless it fits."},"slot":{"oneOf":[{"$ref":"#/components/schemas/Slot"},{"type":"null"}]}}},"Site":{"type":"object","description":"One road-accessible dark-sky site on one night.","required":["site_id","lat","lon","light_pollution_zone","clear_sky_odds","from_point_km","verdict","why","clear_from","clear_until","clear_dark_hours","moonless_clear_dark_hours"],"properties":{"site_id":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"},"light_pollution_zone":{"type":"string","description":"pristine, excellent or rural."},"clear_sky_odds":{"type":["number","null"],"minimum":0,"maximum":1,"description":"Share of dark hours that were clear here in this calendar month, historically."},"from_point_km":{"type":"number"},"verdict":{"$ref":"#/components/schemas/Verdict"},"why":{"type":"string"},"clear_from":{"type":["string","null"],"format":"date-time","description":"Start of the first clear dark hour, or of the first clear twilight hour when none is dark; null when none is clear."},"clear_until":{"type":["string","null"],"format":"date-time","description":"End of the last clear dark hour, or clear twilight hour when none is dark. The hours between may have gaps."},"clear_dark_hours":{"type":"integer"},"moonless_clear_dark_hours":{"type":"integer"}}},"PlanDay":{"type":"object","required":["date","sunrise","sunset","moon_illumination"],"properties":{"date":{"type":"string","format":"date"},"sunrise":{"type":["string","null"],"format":"date-time","description":"Sunrise at the point; null in polar day or night."},"sunset":{"type":["string","null"],"format":"date-time"},"moon_illumination":{"type":"number","minimum":0,"maximum":1,"description":"The moon's lit fraction at local midnight after the evening."},"walks":{"type":"array","items":{"$ref":"#/components/schemas/Walk"},"description":"Walks that fit, go first. Today counts only hours not yet begun. Absent when none do."},"walk_note":{"type":"string","description":"Why walks is empty."},"stargazing":{"type":"array","items":{"$ref":"#/components/schemas/Site"},"description":"Sites for the night of this evening, go first, at least 5 km apart. Absent when none suit."},"stargazing_note":{"type":"string","description":"Why stargazing is empty."}}},"PlanResult":{"type":"object","required":["query","days"],"properties":{"query":{"type":"object","description":"The inputs as used, after defaults and rounding.","required":["lat","lon","radius_km","from","to","activities","limit"],"properties":{"lat":{"type":"number"},"lon":{"type":"number"},"radius_km":{"type":"number"},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"},"activities":{"type":"array","items":{"type":"string","enum":["walk","stargaze"]}},"min_duration_h":{"type":"number"},"max_duration_h":{"type":"number"},"min_length_km":{"type":"number"},"max_length_km":{"type":"number"},"max_ascent_m":{"type":"integer"},"max_rain_mm_h":{"type":"number"},"max_wind_kmh":{"type":"number"},"max_gust_kmh":{"type":"number"},"max_rain_chance_pct":{"type":"number"},"min_temp_c":{"type":"number"},"limit":{"type":"integer"}}},"days":{"type":"array","items":{"$ref":"#/components/schemas/PlanDay"}}}},"PlanResponse":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/PlanResult"},"meta":{"$ref":"#/components/schemas/Meta"}}},"CheckItem":{"type":"object","required":["activity","date"],"properties":{"activity":{"type":"string","enum":["walk","stargaze"]},"date":{"type":"string","format":"date","description":"The day (YYYY-MM-DD, UK date), from today up to 13 days ahead; for stargazing, the evening the night starts, so before 09:00 the night under way is yesterday's date."},"walk_name":{"type":"string","maxLength":100,"description":"For a walk: words from its Walkhighlands name. Accents optional."},"walk_id":{"type":"string","description":"For a walk: its walk_id from plan, instead of walk_name."},"lat":{"type":"number","minimum":54.5,"maximum":61.0,"description":"For stargazing: latitude in decimal degrees."},"lon":{"type":"number","minimum":-9.0,"maximum":-0.5,"description":"For stargazing: longitude in decimal degrees."}},"additionalProperties":false},"CheckRequest":{"type":"object","required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/CheckItem"}},"max_rain_mm_h":{"type":"number","minimum":0,"maximum":10,"description":"Walks: an hour counts as good only with at most this much rain, in mm. 0 means dry; the default 2 allows light rain; higher accepts wetter hours."},"max_wind_kmh":{"type":"number","minimum":0,"maximum":120,"description":"Walks: an hour counts as good only with mean wind at most this, in km/h, at the walk's start. Default 80."},"max_gust_kmh":{"type":"number","minimum":0,"maximum":150,"description":"Walks: an hour counts as good only with gusts at most this, in km/h. Default none."},"max_rain_chance_pct":{"type":"number","minimum":0,"maximum":100,"description":"Walks: an hour counts as good only with at most this chance of rain, in percent. Default none."},"min_temp_c":{"type":"number","minimum":-30,"maximum":30,"description":"Walks: an hour counts as good only at or above this temperature, in degrees C. Default none."}},"additionalProperties":false},"OtherDay":{"type":"object","required":["date","verdict","why"],"properties":{"date":{"type":"string","format":"date"},"verdict":{"$ref":"#/components/schemas/Verdict"},"why":{"type":"string"}}},"CheckResultItem":{"type":"object","required":["activity","date"],"properties":{"activity":{"type":"string","enum":["walk","stargaze"]},"date":{"type":"string","format":"date"},"verdict":{"$ref":"#/components/schemas/Verdict"},"why":{"type":"string"},"error":{"type":"string","description":"Set, with the rest absent, when the item cannot be resolved: an unknown walk, or no dark-sky site within 30 km."},"walk":{"allOf":[{"$ref":"#/components/schemas/Walk"},{"type":"object","properties":{"also_matched":{"type":"array","items":{"type":"string"},"description":"Other walks walk_name matched, best first."}}}]},"night":{"allOf":[{"$ref":"#/components/schemas/Site"},{"type":"object","properties":{"site_note":{"type":"string","description":"Set when no site was within 10 km of the point."}}}]},"sky":{"$ref":"#/components/schemas/Sky"},"other_days":{"type":"array","items":{"$ref":"#/components/schemas/OtherDay"},"description":"Other forecast days that suit it, go or marginal."},"nearby_walks":{"type":"array","items":{"$ref":"#/components/schemas/Walk"},"description":"Up to two walks of similar length (half to one and a half times the time) starting within 20 km that fit better the same day, nearest first."},"nearby_nights":{"type":"array","items":{"$ref":"#/components/schemas/Site"},"description":"Up to two better dark-sky sites within 30 km, the same night."}}},"CheckResult":{"type":"object","required":["items"],"properties":{"limits":{"type":"object","description":"The weather limits used, when any were given.","required":[],"properties":{"max_rain_mm_h":{"type":"number"},"max_wind_kmh":{"type":"number"},"max_gust_kmh":{"type":"number"},"max_rain_chance_pct":{"type":"number"},"min_temp_c":{"type":"number"}}},"items":{"type":"array","items":{"$ref":"#/components/schemas/CheckResultItem"},"description":"One answer per item, in order."}}},"CheckResponse":{"type":"object","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/CheckResult"},"meta":{"$ref":"#/components/schemas/Meta"}}}}},"info":{"title":"Scotland outdoors API","version":"v1","summary":"Read-only answers to Scottish trip questions: which walks fit the weather and where the night sky will be dark and clear, with a go, marginal, no or unknown verdict.","description":"Two question-shaped endpoints over forecast and climatology engines: plan (what to do near a place over the next few days) and check (whether a given walk or night is on). Every response carries a provenance envelope (sources, attribution, forecast fetch time, method). Results and radius are capped; there is no bulk listing. Bulk extraction or redistribution of responses is not permitted.","license":{"name":"Responses carry the source licences named in meta.sources"}},"openapi":"3.1.0","paths":{"/v1/plan":{"get":{"operationId":"plan","summary":"What to do near a place over the next few days","description":"For each day from `from` to `to`: sunrise, sunset and the moon at the point, then the walks that fit the weather and the night's best stargazing within radius_km, each with a go or marginal verdict and a one-line why; when none suit, walk_note or stargazing_note says why. Walks are Walkhighlands routes whose start is within the radius and whose best good-weather slot fits their estimated time (80% of it in good hours, two hours of credit for walks over seven hours); go before marginal, then the longest slot, then the nearest. Stargazing sites are road-accessible points in the darkest light-pollution zones, ranked by clear dark hours weighted by the moon. Walk weather is forecast at each walk's start, not on summits.","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","minimum":54.5,"maximum":61.0},"description":"Latitude, decimal degrees. Rounded to 0.01."},{"name":"lon","in":"query","required":true,"schema":{"type":"number","minimum":-9.0,"maximum":-0.5},"description":"Longitude, decimal degrees. Rounded to 0.01."},{"name":"radius_km","in":"query","schema":{"type":"number","minimum":1,"maximum":100,"default":30}},{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"First day (UK date), from today up to 13 days ahead. A day's stargazing is the night of its evening; before 09:00 the night under way is yesterday's, which from may also be when stargazing is planned, and is by default. Otherwise the default is today."},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Last day; a plan covers at most 7 days. Default two days after today, or after from when that is later."},{"name":"activities","in":"query","schema":{"type":"array","items":{"type":"string","enum":["walk","stargaze"]}},"style":"form","explode":false,"description":"Comma-separated or repeated. Default both."},{"name":"min_duration_h","in":"query","schema":{"type":"number","minimum":0.5,"maximum":24},"description":"Only walks whose estimated time is at least this many hours."},{"name":"max_duration_h","in":"query","schema":{"type":"number","minimum":0.5,"maximum":24},"description":"Only walks whose estimated time is at most this many hours."},{"name":"min_length_km","in":"query","schema":{"type":"number","minimum":0.5,"maximum":100},"description":"Only walks at least this long, in km."},{"name":"max_length_km","in":"query","schema":{"type":"number","minimum":0.5,"maximum":100},"description":"Only walks at most this long, in km."},{"name":"max_ascent_m","in":"query","schema":{"type":"integer","minimum":1,"maximum":3000},"description":"Only walks with at most this much ascent, in metres."},{"name":"max_rain_mm_h","in":"query","schema":{"type":"number","minimum":0,"maximum":10},"description":"Walks: an hour counts as good only with at most this much rain, in mm. 0 means dry; the default 2 allows light rain; higher accepts wetter hours."},{"name":"max_wind_kmh","in":"query","schema":{"type":"number","minimum":0,"maximum":120},"description":"Walks: an hour counts as good only with mean wind at most this, in km/h, at the walk's start. Default 80."},{"name":"max_gust_kmh","in":"query","schema":{"type":"number","minimum":0,"maximum":150},"description":"Walks: an hour counts as good only with gusts at most this, in km/h. Default none."},{"name":"max_rain_chance_pct","in":"query","schema":{"type":"number","minimum":0,"maximum":100},"description":"Walks: an hour counts as good only with at most this chance of rain, in percent. Default none."},{"name":"min_temp_c","in":"query","schema":{"type":"number","minimum":-30,"maximum":30},"description":"Walks: an hour counts as good only at or above this temperature, in degrees C. Default none."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":5,"default":3},"description":"Options per activity per day."}],"responses":{"200":{"description":"The plan, one entry per day.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanResponse"}}}},"304":{"description":"Not modified (If-None-Match matched)."},"400":{"$ref":"#/components/responses/BadRequest"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/v1/check":{"post":{"operationId":"check","summary":"Whether a plan is on","description":"For each item (a walk on a date, or stargazing at a point on a night) a verdict and a one-line why, the timing (the walk's slot and start window, or the clear window at the best dark-sky site within 10 km, widening to 30 km), sunrise, sunset and the moon, the other forecast days that suit it, and better options nearby the same day. A walk is found by walk_id, or by walk_name (every word must start a word of the Walkhighlands name). An item that cannot be resolved carries error and no verdict; the others still answer. Answers are not cached; for a cacheable question use plan.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckRequest"}}}},"responses":{"200":{"description":"One answer per item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/v1/method":{"get":{"operationId":"method","summary":"How the answers are computed","responses":{"200":{"description":"Method description.","content":{"text/markdown":{"schema":{"type":"string"}}}}}}},"/v1/openapi.json":{"get":{"operationId":"openapi","summary":"This document","responses":{"200":{"description":"OpenAPI 3.1 document.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"servers":[{"url":"https://jomcgi.dev/scotland"}]}