Schedule icon
Get icon
Loop icon
Request icon
Write icon
Script icon
Process icon
If icon
AllowFailure icon
SlackIncomingWebhook icon
Return icon
Log icon
Set icon

Route National Weather Service Severe Alerts for Your Sites to Slack, with Update and Cancellation Tracking

Poll api.weather.gov for active alerts at each site, track VTEC threads in KV so re-issues do not re-page, and route Severe alerts and their updates to Slack.

Categories
Infrastructure

Facilities, logistics and field operations teams need to know when the National Weather Service issues a warning for a warehouse, a store, a cell site or a field office, and they need to know once. The public NWS API returns every active alert for a point, but the feed is noisy to poll: a Coastal Flood Warning is re-issued as a new CAP message with a new id every few hours even when nothing changed, one alert covers several of your sites, watches are upgraded to warnings, and alerts end either by cancellation or by dropping off the feed. This flow polls api.weather.gov for each site, groups messages into alert threads by VTEC event number (or by the CAP references chain when there is no VTEC), remembers the threads in the KV store and routes only what changed: NEW, UPDATED (severity, event, end time or affected sites changed), UPGRADED, CANCELLED, EXPIRED and ENDED. Extreme and Severe alerts go to Slack, everything else is logged.

How it works

  1. A Schedule trigger runs every 10 minutes. Flow concurrency is 1, so a slow run never overlaps the next one and reads stale state.
  2. previous_state (KV Get, errorOnMissing: false) reads the alert threads routed by earlier runs. It is missing on the first run, which is reported as a BASELINE run: every active alert is NEW once.
  3. fetch_sites (Loop, two requests at a time) calls /alerts/active?status=actual&point=lat,lon for each entry of the sites JSON input, with the User-Agent the NWS requires (contact input). status=actual drops test and exercise messages. A 400 (point outside the NWS area) is accepted and reported per site; 5xx and timeouts are retried three times with exponential backoff, then tolerated so one failed site does not hide the others. The Loop outputs collect site, status code and body, and responses (Storage Write) puts them in one file.
  4. route (Python, standard library only) merges alerts by id so an alert covering several sites is one item, threads them by VTEC office.phenomenon.significance.ETN (secondary UPG, CAN and EXP VTEC lines close the thread they name) and compares each thread with the stored one. An unchanged CON re-issue is absorbed silently; a changed severity, event, end time or set of affected sites is UPDATED; a Cancel message or a CAN/EXP action is CANCELLED or EXPIRED; a thread that dropped off the feed is ENDED, or UPGRADED when a newer alert replaced it. A failed fetch is never read as an all-clear: those threads are kept unchanged until the site answers again, and a site removed from the input is forgotten without a message. The step writes site_alerts.csv, logs one line per notification (WARN when routed) and returns the summary, the notifications with ready Slack text, and the new state.
  5. dispatch (If) runs each_notification (Loop) when there is something to say. by_severity (If) sends the alert to Slack when its current or previous severity is in route_severities, so the downgrade or cancellation of a Severe alert is routed too; other alerts take the log_other branch. The Slack call sits in an AllowFailure block with a sent marker, and delivery_report counts SLACK_SENT, SLACK_FAILED and LOGGED from the Loop outputs.
  6. save_state (KV Set) runs after dispatch, so a run that dies mid-way repeats its notifications rather than losing them. warn_sites names out-of-bounds and failed sites, summary logs the counts, and the flow exposes the summary, the notifications and the CSV as outputs.

What you get

  • One Slack message per new or materially changed Extreme or Severe alert, listing every affected site, with severity, urgency, certainty, area, onset, end time, the NWS instruction and a link to the alert record.
  • No repeat pages when the NWS re-issues an unchanged warning, and an explicit all-clear (CANCELLED, EXPIRED, ENDED) for alerts that were routed.
  • A per-site CSV each run: every active alert with its thread, VTEC action, severity and times, plus a row for each site with no alerts, out of bounds or failed.
  • OUT_OF_BOUNDS for points the NWS does not cover, so a wrong coordinate is visible instead of silently clean.

Who it's for

  • Facilities, logistics and retail operations teams with sites across the US.
  • Telecom, utility and field service teams that stage crews ahead of storms, floods and freezes.
  • Event, construction and outdoor operations teams that want a reliable severe-weather feed in Slack.

Why orchestrate this with Kestra

  • HTTP retries, timeouts and tolerated status codes are declared per task, so one flaky request does not stop the check for every other site.
  • The KV store keeps the alert threads between runs, so "notify once, notify on change" needs no database.
  • Loop and If make the routing visible in the topology: fetch per site, then one branch per notification, Slack or log.
  • Flow concurrency and the schedule replace a cron job plus a lock file.

Prerequisites

  • Kestra 2.0 with the Python scripts plugin and the Slack plugin (io.kestra.plugin.slack.notifications.SlackIncomingWebhook).
  • Python 3.9 or newer on the worker for the Process task runner (standard library only, no packages). To run in a container instead, use the io.kestra.plugin.scripts.runner.docker.Docker task runner with containerImage: python:3.12-slim.
  • Outbound HTTPS to api.weather.gov. No API key; set contact to a real email or URL, as the NWS asks for one in the User-Agent.
  • Sites inside the NWS area: the 50 states, US territories and coastal waters.

Secrets

  • SLACK_WEBHOOK_URL: Slack incoming webhook for routed alerts (optional; without it delivery_report warns SLACK_FAILED and the alerts stay in the logs, the outputs and the CSV).

Quick start

  1. Import the flow and click Execute with the defaults: seven sites on the East Coast, in Alaska, Denver and Chicago. The first run is a BASELINE and reports every active alert as NEW; which sites have alerts depends on the day's weather.
  2. Execute again right away: No new, changed or cleared alerts, because unchanged re-issues are absorbed.
  3. Add a second site next to an existing one and a point outside the US such as London: the shared alerts come back as UPDATED with now also affects ..., and London is reported as OUT_OF_BOUNDS.
  4. Delete the KV key nws_alert_router_state and run with route_severities set to ["Extreme", "Severe", "Moderate"] to route watches and advisories too.
  5. Replace sites with your own locations, set contact, add SLACK_WEBHOOK_URL as a secret and leave the schedule on.

How to extend

  • Keep the site list in a namespace file or a database table and read it there instead of in an input.
  • Send Extreme alerts to PagerDuty or Opsgenie and Severe ones to Slack with a Switch on severity inside the Loop.
  • Filter on urgency and certainty (for example only Immediate or Expected) before routing.
  • Append each run's CSV to a warehouse table to keep a history of which sites were under which warnings.
  • Run one copy per region with its own state_key and Slack channel.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.