14 KiB
Event Wire Contract
This document is the canonical JSON contract for events emitted by
weatherfeeder JSON sinks, including stdout and NATS. Postgres stores the same
event envelope fields in parent table columns; see
Postgres integration.
Downstream consumers should read the envelope, switch on schema, and decode
payload according to that schema.
Envelope
Every emitted event is a JSON object with these fields:
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | Stable event identifier. Treat as opaque. |
kind |
string | yes | Routing kind, such as observation or alert. |
source |
string | yes | Configured source name. |
emitted_at |
timestamp | yes | When the daemon emitted the event. |
effective_at |
timestamp | no | Timestamp the payload is about, when known. |
schema |
string | no | Schema identifier. Weatherfeeder sources and normalizers set this. |
payload |
object, array, string, or scalar | yes | Schema-specific payload. |
Timestamps are JSON strings using Go time.Time JSON encoding, which is
RFC3339Nano-compatible. Weatherfeeder normalizers use UTC timestamps for
canonical payloads.
Kinds And Schemas
Canonical schemas emitted after normalization:
| Kind | Schema | Payload |
|---|---|---|
observation |
weather.observation.v1 |
WeatherObservation |
forecast |
weather.forecast.v1 |
WeatherForecastRun |
forecast_discussion |
weather.forecast_discussion.v1 |
WeatherForecastDiscussion |
weather_story |
weather.weather_story.v1 |
WeatherStoryRun |
alert |
weather.alert.v1 |
WeatherAlertRun |
outlook |
weather.outlook.v2 |
WeatherOutlookRun |
Raw upstream schemas emitted by current sources:
| Kind | Schema | Payload |
|---|---|---|
observation |
raw.nws.observation.v1 |
NWS observation JSON |
observation |
raw.openmeteo.current.v1 |
Open-Meteo current JSON |
observation |
raw.openweather.current.v1 |
OpenWeather current JSON |
forecast |
raw.nws.hourly.forecast.v1 |
NWS hourly forecast JSON |
forecast |
raw.nws.narrative.forecast.v1 |
NWS narrative forecast JSON |
forecast_discussion |
raw.nws.forecast_discussion.v1 |
NWS forecast discussion HTML string |
weather_story |
raw.nws.weatherstories.v1 |
NWS weather stories JSON |
forecast |
raw.openmeteo.hourly.forecast.v1 |
Open-Meteo hourly forecast JSON |
alert |
raw.nws.alerts.v1 |
NWS alerts JSON |
outlook |
raw.spc.convective_outlook.v1 |
SPC convective outlook raw bundle |
standards.SchemaRawOpenWeatherHourlyForecastV1 exists in code, but no current
registered source emits it.
Shared Conventions
- Canonical numeric measurements use metric units.
- Floating-point values in canonical payloads are rounded to 4 digits after the decimal point during normalization.
- Optional fields use JSON
omitempty; absent fields should be treated as unknown. conditionCodeis a WMO weather interpretation code. Unknown observation conditions use-1. Forecast periodconditionCodeis optional.- Additive fields are compatible within a schema version. Removing, renaming, or changing the meaning of a field requires a new schema identifier.
weather.observation.v1
Payload type: WeatherObservation.
| Field | Type | Required | Notes |
|---|---|---|---|
stationId |
string | no | Provider station/location identifier. |
stationName |
string | no | Human station name. |
timestamp |
timestamp | yes | Observation timestamp. |
conditionCode |
integer | yes | WMO code; -1 means unknown. |
isDay |
boolean | no | Day/night hint. |
textDescription |
string | no | Short human description. |
temperatureC |
number | no | Celsius. |
dewpointC |
number | no | Celsius. |
windDirectionDegrees |
number | no | Degrees. |
windSpeedKmh |
number | no | Kilometers per hour. |
windGustKmh |
number | no | Kilometers per hour. |
barometricPressurePa |
number | no | Pascals. |
visibilityMeters |
number | no | Meters. |
relativeHumidityPercent |
number | no | Percent from 0 to 100. |
apparentTemperatureC |
number | no | Celsius. |
presentWeather |
array | no | Provider-specific present weather fragments. |
presentWeather[] entries contain optional raw objects.
weather.forecast.v1
Payload type: WeatherForecastRun.
| Field | Type | Required | Notes |
|---|---|---|---|
locationId |
string | no | Provider location identifier. |
locationName |
string | no | Human location name. |
issuedAt |
timestamp | yes | When the forecast run was generated or issued. |
updatedAt |
timestamp | no | Subsequent provider update time. |
product |
string | yes | Current emitted values are hourly and narrative. |
latitude |
number | no | Degrees. |
longitude |
number | no | Degrees. |
elevationMeters |
number | no | Meters. |
periods |
array | yes | Ordered forecast periods. |
periods[] entries:
| Field | Type | Required | Notes |
|---|---|---|---|
startTime |
timestamp | yes | Period start. |
endTime |
timestamp | yes | Period end. |
name |
string | no | Human label. |
isDay |
boolean | no | Day/night hint. |
conditionCode |
integer | no | WMO code when applicable. |
textDescription |
string | no | Human summary. |
temperatureC |
number | no | Celsius. |
temperatureCMin |
number | no | Celsius. |
temperatureCMax |
number | no | Celsius. |
dewpointC |
number | no | Celsius. |
relativeHumidityPercent |
number | no | Percent from 0 to 100. |
windDirectionDegrees |
number | no | Degrees. |
windSpeedKmh |
number | no | Kilometers per hour. |
windGustKmh |
number | no | Kilometers per hour. |
barometricPressurePa |
number | no | Pascals. |
visibilityMeters |
number | no | Meters. |
apparentTemperatureC |
number | no | Celsius. |
cloudCoverPercent |
number | no | Percent from 0 to 100. |
probabilityOfPrecipitationPercent |
number | no | Percent from 0 to 100. |
precipitationAmountMm |
number | no | Liquid-equivalent millimeters. |
snowfallDepthMm |
number | no | Millimeters. |
uvIndex |
number | no | Unitless index. |
weather.forecast_discussion.v1
Payload type: WeatherForecastDiscussion.
| Field | Type | Required | Notes |
|---|---|---|---|
officeId |
string | no | NWS office identifier. |
officeName |
string | no | Office name. |
product |
string | yes | Current value is afd. |
issuedAt |
timestamp | yes | Bulletin issue time. |
updatedAt |
timestamp | no | Subsequent update time. |
keyMessages |
array of strings | no | Extracted key messages. |
shortTerm |
object | no | Short-term section. |
longTerm |
object | no | Long-term section. |
shortTerm and longTerm sections contain optional qualifier, issuedAt,
and text fields.
weather.weather_story.v1
Payload type: WeatherStoryRun.
| Field | Type | Required | Notes |
|---|---|---|---|
officeId |
string | no | NWS office identifier. |
asOf |
timestamp | yes | Snapshot time. |
stories |
array | yes | Ordered story cards. |
stories[] entries:
| Field | Type | Required | Notes |
|---|---|---|---|
officeId |
string | no | Office identifier. |
startTime |
timestamp | yes | Story start. |
endTime |
timestamp | yes | Story end. |
updatedAt |
timestamp | yes | Story update time. |
title |
string | no | Story title. |
description |
string | no | Story description. |
altText |
string | no | Image alternate text. |
priority |
boolean | yes | Provider priority flag. |
order |
integer | yes | Provider display order. |
downloadUrl |
string | no | Story image URL. |
weather.alert.v1
Payload type: WeatherAlertRun.
| Field | Type | Required | Notes |
|---|---|---|---|
locationId |
string | no | Provider location identifier. |
locationName |
string | no | Human location name. |
asOf |
timestamp | yes | Snapshot time. |
latitude |
number | no | Degrees. |
longitude |
number | no | Degrees. |
alerts |
array | yes | Active alerts. |
alerts[] entries:
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | Provider-stable alert identifier. |
event |
string | no | Alert event label. |
headline |
string | no | Alert headline. |
severity |
string | no | Provider severity. |
urgency |
string | no | Provider urgency. |
certainty |
string | no | Provider certainty. |
status |
string | no | Alert status. |
messageType |
string | no | Alert message type. |
category |
string | no | Alert category. |
response |
string | no | Recommended response. |
description |
string | no | Alert description. |
instruction |
string | no | Alert instruction. |
sent |
timestamp | no | Provider sent time. |
effective |
timestamp | no | Effective time. |
onset |
timestamp | no | Alert period start. |
ends |
timestamp | no | Alert period end. |
expires |
timestamp | no | Provider expiration metadata; not necessarily the alert period end. |
areaDescription |
string | no | Affected area description. |
senderName |
string | no | Provider sender name. |
references |
array | no | Related alerts. |
references[] entries contain optional id, identifier, sender, and
sent fields.
weather.outlook.v2
Payload type: WeatherOutlookRun.
The current producer is the SPC convective outlook normalizer. It emits Day 1-3 convective outlook snapshots for categorical, tornado, hail, and wind products that apply to the configured forecast point. Raw SPC bundles remain complete; canonical outlook payloads are filtered to local polygons. All timestamps are UTC.
| Field | Type | Required | Notes |
|---|---|---|---|
locationId |
string | no | Operator-configured location identifier. |
locationName |
string | no | Operator-configured location label. |
latitude |
number | no | Configured point latitude in decimal degrees. |
longitude |
number | no | Configured point longitude in decimal degrees. |
asOf |
timestamp | yes | Snapshot time. For SPC, this is the latest outlook issue time when available. |
issuedAt |
timestamp | no | Latest issue time across outlook features when any feature exists. |
outlooks |
array | yes | Ordered outlook polygons containing the configured point. |
discussions |
array | yes | Run-level day discussions for retained outlook days. |
outlooks[] entries:
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | Deterministic weatherfeeder outlook identifier. |
provider |
string | yes | Current value is spc. |
product |
string | yes | Current value is convective. |
day |
integer | yes | SPC outlook day, currently 1, 2, or 3. |
outlookType |
string | yes | categorical, tornado, hail, or wind. |
label |
string | yes | SPC outlook label such as SLGT or 15. |
labelText |
string | no | Human label text from SPC, when present. |
severityRank |
integer | no | SPC DN value, when present. |
validFrom |
timestamp | yes | Valid period start. |
validTo |
timestamp | yes | Valid period end. |
issuedAt |
timestamp | yes | Feature issue time. |
expiresAt |
timestamp | yes | Expiration time; currently equal to validTo. |
forecaster |
string | no | SPC forecaster text, when present. |
sourceUrl |
string | no | GeoJSON product URL for this outlook feature. |
imageUrl |
string | no | Reserved for provider image URLs; currently empty. |
containsLocation |
boolean | yes | Always true for emitted v2 outlooks. |
geometry |
object | yes | Compact GeoJSON Polygon or MultiPolygon geometry. |
geometry preserves the SPC feature geometry as compact GeoJSON using
[longitude, latitude] coordinate order. containsLocation is computed with
that geometry and the configured source latitude/longitude; boundary points
count as contained. Polygons that do not contain the configured point are not
included in canonical v2 payloads.
discussions[] entries:
| Field | Type | Required | Notes |
|---|---|---|---|
day |
integer | yes | SPC outlook day, currently 1, 2, or 3. |
headline |
string | no | Matching Day 1-3 print-page product title. |
summary |
string | no | Text from the print-page ...SUMMARY... section. |
discussion |
string | no | Cleaned full print-page product text. |
updatedAt |
timestamp | no | Print-page update time, when present. |
When no SPC polygons apply locally, the run is still emitted with outlooks: []
and discussions: []. Discussions are included only for days represented by at
least one retained outlook, and multiple retained outlook types for the same day
share one discussion entry.
SPC Outlook Supersession
Consumers should prefer latest-run semantics for current conditions: read the
latest WeatherOutlookRun for the configured location and use its outlooks
and discussions arrays together.
Historical SQL consumers that collapse older rows should identify superseded
outlooks by provider, product, outlookType, validFrom, and validTo,
then keep rows with the greatest issuedAt. day and label are not identity
fields. When multiple retained polygons share that latest issuedAt, preserve
the full group.
Legacy weather.outlook.v1
weather.outlook.v1 is a historical canonical schema retained as a standards
constant for older data and consumers. Current SPC normalization emits
weather.outlook.v2.
The v1 payload used WeatherOutlookRun and placed headline, summary, and
discussion on each outlooks[] polygon. It also represented the pre-v2 SPC
canonical behavior, where national polygons were preserved in canonical output.
Compact Example
{
"id": "NWSObservationKSTL:2026-06-10T12:00:00Z",
"kind": "observation",
"source": "NWSObservationKSTL",
"emitted_at": "2026-06-10T12:00:05Z",
"effective_at": "2026-06-10T12:00:00Z",
"schema": "weather.observation.v1",
"payload": {
"stationId": "KSTL",
"timestamp": "2026-06-10T12:00:00Z",
"conditionCode": 0,
"temperatureC": 22.5
}
}