Skip to main content
For Developers

Webhooks

Get pushed snow-report updates and powder alerts the moment conditions change — signed with HMAC-SHA256 so you can trust every delivery.

Events

snow-report.updated

A watched resort's snow report changed (new snowfall, depth, or report timestamp). Global subscriptions (no resortSlug) fire when the recent-snowfall leaderboard changes.

powder-alert

A resort recorded 15 cm+ of snowfall in the last 24 hours. Sent at most once per resort per day.

Subscribe

POST your https callback URL and the events you want. Scope to one resort with resortSlug, or omit it for global events. No API key needed — the response includes a per-subscription secret.

POST /api/v1/webhooks

curl -X POST https://www.snowsure.ai/api/v1/webhooks \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/snowsure",
    "events": ["snow-report.updated", "powder-alert"],
    "resortSlug": "aspen-mountain"
  }'

// 201 →
// {
//   "data": {
//     "id": "1f6b0d3e-...",
//     "secret": "d41d8cd98f00b204e9800998ecf8427e...",  ← shown ONCE, store it
//     "url": "https://example.com/hooks/snowsure",
//     "events": ["snow-report.updated", "powder-alert"],
//     "resortSlug": "aspen-mountain",
//     "createdAt": "2026-07-14T12:00:00.000Z"
//   }
// }

Deliveries

Events are POSTed to your URL as JSON with a 10-second timeout and one retry. After 8 consecutive failed deliveries the subscription is dropped.

Example delivery:

POST <your url>
Content-Type: application/json
X-SnowSure-Event: powder-alert
X-SnowSure-Signature: sha256=8f2c1a...
X-SnowSure-Delivery: 7c9e6679-...

{
  "id": "e6a7c2d1-...",
  "event": "powder-alert",
  "resortSlug": "aspen-mountain",
  "data": {
    "slug": "aspen-mountain",
    "name": "Aspen Mountain",
    "snowfall24hCm": 22,
    "snowDepthCm": 145,
    "thresholdCm": 15,
    "url": "https://www.snowsure.ai/resorts/aspen-mountain"
  },
  "createdAt": "2026-07-14T12:30:00.000Z",
  "subscriptionId": "1f6b0d3e-..."
}

Verify signatures

Every delivery carries an X-SnowSure-Signature header: sha256=HEX(HMAC-SHA256(secret, rawBody)). Recompute it over the raw request body with your subscription secret and compare in constant time. Reject anything that doesn't match.

Node.js verification:

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

function verify(rawBody: string, signatureHeader: string, secret: string) {
  const expected = 'sha256=' +
    createHmac('sha256', secret).update(rawBody).digest('hex')
  return (
    expected.length === signatureHeader.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
  )
}

// In your handler:
// verify(rawBody, req.headers['x-snowsure-signature'], SECRET)

Manage subscriptions

Use the secret returned at subscribe time as a bearer token. Secrets are never stored in plaintext on our side — keep yours safe; it cannot be recovered.

# Inspect a subscription
curl https://www.snowsure.ai/api/v1/webhooks/{id} \
  -H "Authorization: Bearer <secret>"

# Unsubscribe
curl -X DELETE https://www.snowsure.ai/api/v1/webhooks/{id} \
  -H "Authorization: Bearer <secret>"

Limits & behavior

  • • Callback URLs must be public https endpoints.
  • • Max 10 new subscriptions per hour per IP.
  • • Delivery: 10s timeout, one retry, drop after 8 consecutive failures.
  • • Powder alerts: at most one per resort per UTC day (15 cm/24h threshold).
  • • Events are evaluated on a periodic schedule, not per-second.

More for developers