Aeroza

API reference

Every public route on the Aeroza v1 API, organised by domain. Use this page when you want a one-screen survey of the surface; use the interactive explorer when you want to inspect schemas and send requests directly from the page.

Three ways to drive the API interactively:

Conventions

Health & meta

RouteSummary
GET /health
Liveness check.
Returns version + status. No DB or NATS dependency.
GET /v1/stats
Compact 'what does the system know right now?' snapshot.
Alert counts, MRMS file/grid counts, freshness watermarks.
GET /v1/me
Introspect the calling API key (name, owner, scopes, last-used).
Requires Authorization: Bearer aza_live_*. Mint keys with the aeroza-api-keys CLI; the operator's only management plane in v1.

Alerts

RouteSummary
GET /v1/alerts
List active NWS alerts as a GeoJSON FeatureCollection.
Filter by point, bbox, or minimum severity.
GET /v1/alerts/stream
Server-Sent Events feed re-emitting newly observed alerts.
One event per row published on aeroza.alerts.nws.new.
GET /v1/alerts/{alert_id}
Single-alert detail with description + instruction.
Includes alerts whose `expires` is in the past.

MRMS — catalog & grids

RouteSummary
GET /v1/mrms/files
MRMS file catalog — what data is available right now.
Filter by product, level, and a [since, until) window.
GET /v1/mrms/grids
Materialised-grid catalog — what data is queryable right now.
Same filters as /v1/mrms/files; result is locator-shaped.
GET /v1/mrms/grids/{file_key}
One materialised grid by its source S3 key.

MRMS — queries

RouteSummary
GET /v1/mrms/grids/sample
Nearest-cell value at a (lat, lng).
Defaults to the latest grid; pass at_time to reduce a grid valid at-or-before that moment.
GET /v1/mrms/grids/polygon
Reduce a grid over a polygon (max / mean / min / count_ge).
Polygon vertices on lng,lat,lng,lat,…; ring implicitly closed; count_ge requires a numeric threshold.
GET /v1/mrms/tiles/{z}/{x}/{y}.png
Web-Mercator XYZ raster tile of the latest matching MRMS grid.
256×256 PNG with the standard NWS dBZ ramp; transparent fallback when no grid is materialised. Pass fileKey to pin a specific source. Cache-Control: max-age=60.

METAR (surface observations)

RouteSummary
GET /v1/metar
List recent METAR observations, newest first.
Filter by station (case-insensitive ICAO id), since, until, bbox (min_lng,min_lat,max_lng,max_lat), and limit (default 100, max 500). Sourced from aviationweather.gov; rows include parsed temp/wind/visibility plus the raw METAR text for custom parsers.
GET /v1/metar/{station_id}/latest
Most-recent observation for one ICAO station.
Case-insensitive on the path. 404 when the station has no observations.

Nowcasts & calibration

RouteSummary
GET /v1/nowcasts
Predicted-grid catalog (algorithm × forecast horizon).
Filter by product, level, algorithm (e.g. 'persistence'), horizonMinutes, and a [since, until) window on validAt.
GET /v1/calibration
Aggregate verification metrics, grouped by algorithm × horizon.
Sample-weighted MAE / bias / RMSE plus categorical POD / FAR / CSI (at the verifier's threshold, default 35 dBZ) over the window. Default windowHours=24; supports algorithm / product / level filters.
GET /v1/calibration/series
Time-bucketed companion to /v1/calibration — sparkline per (algorithm, horizon).
Same metrics (continuous + categorical), with one extra group-by on bucketStart. bucketSeconds in [300, 86400] (default 3600 / 1 hour).

Webhooks

RouteSummary
GET /v1/webhooks
List webhook subscriptions.
Filter by status (active / paused / disabled). The list omits the secret; the detail route returns its hash.
POST /v1/webhooks
Create a subscription.
Body: target URL, events array (e.g. aeroza.alerts.nws.new, aeroza.nowcast.grids.new), an optional alertRuleId, optional secret (auto-generated if omitted).
GET /v1/webhooks/{sub_id}
One subscription's full detail.
PATCH /v1/webhooks/{sub_id}
Update events / target / status / alertRuleId.
Status transitions: active ⇄ paused; the dispatcher's circuit breaker can flip a sub to disabled after repeated 4xx/5xx.
DELETE /v1/webhooks/{sub_id}
Soft-delete a subscription.
Sets status to deleted; rows are kept so the delivery log remains queryable.
GET /v1/webhooks/{sub_id}/deliveries
Recent delivery attempts for a subscription.
Read-only audit trail — one row per attempt the dispatcher made (initial + retries), newest-first. Optional status filter (ok / failed / retrying). The signed payload itself is omitted from the wire.

Alert rules (webhook predicate DSL)

RouteSummary
GET /v1/alert-rules
List alert rules.
Rules are reusable across webhook subscriptions — one rule, many subs. The wire shape is a discriminated union over rule.kind (currently 'point' or 'polygon').
POST /v1/alert-rules
Create a rule.
Body: kind, predicate config (point: lat/lng/radiusMeters; polygon: GeoJSON-style coordinates), severity floor, optional event-name allowlist.
GET /v1/alert-rules/{rule_id}
One rule's full detail.
PATCH /v1/alert-rules/{rule_id}
Update predicate / severity floor / status.
DELETE /v1/alert-rules/{rule_id}
Soft-delete a rule.

Stable contract?

v1 is the contract shape we're committing to — JSON envelope, camelCase aliases, GeoJSON ordering. Adding new fields to a response payload is non-breaking; removing or renaming will bump the route to /v2. The TypeScript SDK (@aeroza/sdk) pins the wire types so consumers get a compile-time signal when something changes; the dev console at /console is the same SDK driving every panel, so any awkwardness in the contract shows up in the SDK first.