● Alwaysbeat

Automation

Signed webhooks

A webhook: channel delivers a JSON POST to your endpoint on every state change, signed with HMAC-SHA256 so you can verify it came from Alwaysbeat.

Setup

Add a channel of the form webhook:https://your-endpoint to a check (see Notifications). Only http/https URLs with a public host are accepted — requests to private or internal addresses are refused.

The request

Deliveries are POST with Content-Type: application/json and a User-Agent of dead-mans-fingers-webhook/1. The body is:

{
  "event": "check.state_changed",
  "check_id": "3f8a1c2e-...",
  "name": "nightly-db-backup",
  "status": "down",          // new state
  "previous": "up",          // prior state
  "reason": "missed",        // why it changed
  "account_id": "...",
  "at": "2026-07-20T03:35:00Z"  // RFC 3339, UTC
}

Respond with any 2xx to acknowledge. Non-2xx responses are retried with backoff; 4xx (other than 429) are treated as permanent and not retried.

Verifying the signature

Every delivery carries an X-DMF-Signature header:

X-DMF-Signature: t=1753000500,v1=<hex-hmac-sha256>

where:

To verify: recompute the HMAC over t, a literal ., and the exact raw body, then compare in constant time. Reject the request if it doesn't match, and reject stale timestamps to prevent replays.

# Python (Flask)
import hmac, hashlib, time

def verify(request, secret):
    header = request.headers["X-DMF-Signature"]
    parts = dict(p.split("=", 1) for p in header.split(","))
    ts, sig = parts["t"], parts["v1"]

    signed = f"{ts}.".encode() + request.get_data()   # raw body, unparsed
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return False
    if abs(time.time() - int(ts)) > 300:              # reject >5 min old
        return False
    return True
Getting your signing secret Signatures are keyed with a shared signing secret. Per-account signing secrets are on the roadmap; if you need to verify signatures today, get in touch and we'll provide your key. Until then you can still consume the payload — just restrict the receiving endpoint (e.g. a hard-to- guess path or network allowlist).