Pro Weather

Alert webhooks

The webhook reference for developers - the JSON body an alert sends, the HMAC signature to verify, and the delivery rules.

This is the developer reference for alert webhooks: what arrives at your server, how to prove it came from us, and how delivery behaves. Setting one up takes no code and is covered in Alerts & warnings; come here when you are writing the receiver.

The request

Each notification is a POST with a JSON body:

{
  "version": 1,
  "event": "alert.triggered",
  "firedAt": "2026-08-03T04:12:07.881Z",
  "site": {
    "subdomain": "ardooie",
    "title": "Ardooie Meteo",
    "url": "https://ardooie.pro-weather.com"
  },
  "alert": {
    "id": "clx0alertruleid",
    "metric": "tempLow",
    "label": "Temperature below",
    "kind": "threshold",
    "reminder": false
  },
  "reading": { "value": -1.4, "text": "-1.4 °C", "unit": "°C", "detail": null },
  "threshold": { "value": 0, "text": "0.0 °C" },
  "observedAt": 1754194320
}

reading.value and reading.text are null for an event alert, which reports an occurrence rather than a number; reading.detail carries the specifics (which channel, which record, which upload). alert.reminder is true when the notification is a repeat of a condition already reported.

Verifying the signature

Every request carries three headers:

HeaderMeaning
X-ProWeather-Eventalert.triggered or alert.test
X-ProWeather-TimestampUnix seconds at which we signed the request
X-ProWeather-Signaturesha256= followed by the hex HMAC-SHA256 of <timestamp>.<raw body>, keyed with your signing secret

Compute the same HMAC over the raw body (before any JSON parsing) and compare it with a constant-time comparison. Reject anything whose timestamp is more than a few minutes old, so a captured request cannot be replayed at you.

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, headers, secret) {
  const ts = Number(headers['x-proweather-timestamp']);
  if (!Number.isFinite(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false;
  const expected = `sha256=${createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex')}`;
  const a = Buffer.from(expected);
  const b = Buffer.from(headers['x-proweather-signature'] ?? '');
  return a.length === b.length && timingSafeEqual(a, b);
}

The signing secret is generated for you and shown under Alerts → Webhook. Changing the URL keeps the same secret, so a receiver that already verifies signatures does not break.

Delivery rules

  • The endpoint must be HTTPS and cannot point at a private network address.
  • Redirects are not followed.
  • We wait 6 seconds for a response.
  • There is no retry queue: a failed delivery leaves the alert unlatched, so the next refresh (about 5 minutes) tries again while the condition still holds. A receiver that was briefly down misses nothing that is still true.

Use Send test under Alerts → Webhook to exercise the receiver; the test request is signed exactly like a real one, with X-ProWeather-Event: alert.test.

Next steps

On this page