Signed webhooks for every alert

hostnag posts a signed JSON payload for every alert to any https endpoint you name.

Included on every plan.

PlansFreeProBusiness

Down

Headers
x-hostnag-event
x-hostnag-timestamp
x-hostnag-signature
Body
{
  "version": 4,
  "id": "ev-6",
  "type": "alert.down",
  "severity": "critical",
  "title": "[Critical] https://www.example.com/ is down",
  "summary": "Down from 2 of 3 locations (US East, Frankfurt). Last error: unexpected_status: 503.",
  "createdAt": "2026-09-12T08:00:00.000Z",
  "threshold": null,
  "daysLeft": null,
  "expiresAt": null,
  "details": {
    "downSince": "2026-09-12T07:58:00.000Z",
    "durationMinutes": 2,
    "reminder": false,
    "failedLocations": [
      "iad1",
      "fra1"
    ],
    "totalLocations": 3,
    "lastError": "unexpected_status: 503",
    "statusCode": 503
  },
  "monitor": {
    "id": "mon-3",
    "name": "Marketing site",
    "target": "https://www.example.com/",
    "type": "http",
    "lastError": null,
    "issuer": null,
    "registrar": null,
    "fingerprint256": null
  },
  "group": {
    "name": "Client A"
  },
  "org": {
    "name": "Acme"
  },
  "url": "https://hostnag.com/acme/monitors/mon-1",
  "ackUrl": "https://hostnag.com/ack/abababababababababababababababababababababababababababababababab",
  "acknowledgedAt": null,
  "tier": 0
}

Set up

  1. 01

    Add the endpoint

    Any https:// URL that can receive a POST request.

  2. 02

    Verify each request

    Every POST carries x-hostnag-event, x-hostnag-timestamp and x-hostnag-signature headers: an HMAC-SHA256 sha256= digest over the timestamp and body, version 4 payload. A down or degraded alert also carries details.context: the changes recorded on the same host in the last day and the vendor pages you watch that are in trouble right now. Leave the signing secret blank to have hostnag generate one.

What arrives

Rendered by the same code that sends it.
Payloadversion 4

Back up

Headers
x-hostnag-event
x-hostnag-timestamp
x-hostnag-signature
Body
{
  "version": 4,
  "id": "ev-8",
  "type": "alert.up",
  "severity": "info",
  "title": "[Recovered] https://www.example.com/ is back up after 1 h 15 min",
  "summary": "https://www.example.com/ answered again from every location. It was down for 1 h 15 min.",
  "createdAt": "2026-09-12T08:00:00.000Z",
  "threshold": null,
  "daysLeft": null,
  "expiresAt": null,
  "details": {
    "downSince": "2026-09-12T07:58:00.000Z",
    "durationMinutes": 75
  },
  "monitor": {
    "id": "mon-3",
    "name": "Marketing site",
    "target": "https://www.example.com/",
    "type": "http",
    "lastError": null,
    "issuer": null,
    "registrar": null,
    "fingerprint256": null
  },
  "group": {
    "name": "Client A"
  },
  "org": {
    "name": "Acme"
  },
  "url": "https://hostnag.com/acme/monitors/mon-1",
  "ackUrl": "https://hostnag.com/ack/abababababababababababababababababababababababababababababababab",
  "acknowledgedAt": null,
  "tier": 0
}

DNS answer changed

Headers
x-hostnag-event
x-hostnag-timestamp
x-hostnag-signature
Body
{
  "version": 4,
  "id": "ev-3e",
  "type": "alert.changed",
  "severity": "warning",
  "title": "[Warning] DNS answer changed for api.example.com",
  "summary": "The api.example.com record now answers 198.51.100.7 (was 203.0.113.10), from every location we checked.",
  "createdAt": "2026-09-12T08:00:00.000Z",
  "threshold": null,
  "daysLeft": 60,
  "expiresAt": null,
  "details": {
    "change": "dns_answer",
    "from": [
      "203.0.113.10"
    ],
    "to": [
      "198.51.100.7"
    ]
  },
  "monitor": {
    "id": "mon-1",
    "name": "API",
    "target": "api.example.com",
    "type": "dns",
    "lastError": null,
    "issuer": null,
    "registrar": null,
    "fingerprint256": null
  },
  "group": {
    "name": "Client A"
  },
  "org": {
    "name": "Acme"
  },
  "url": "https://hostnag.com/acme/monitors/mon-1",
  "ackUrl": "https://hostnag.com/ack/abababababababababababababababababababababababababababababababab",
  "acknowledgedAt": null,
  "tier": 0
}

Routing

Every alert follows the group's alert policy: which channels a route goes to and the minimum severity each one takes, in bands of critical, warning and info. Quiet hours hold non-critical alerts for email, Slack, Discord, SMS, Teams and Telegram until the window closes, then deliver one digest; critical alerts are never held, and webhook, PagerDuty and Opsgenie always deliver right away, since each hands the alert on to another system. A maintenance window on a group or a monitor suppresses alerts for its duration while checks keep running. An alert nobody acknowledges escalates through up to three tiers, each after its own number of minutes, and can be acknowledged with one tap from the alert message itself.

Questions

How do I verify a request came from hostnag?

Recompute the HMAC-SHA256 of the timestamp and body with your signing secret and compare it to the sha256= value in x-hostnag-signature.

What happens on a breaking payload change?

The version number in the body increases; version 4 is current.

Watches

Add a host. Hear about it in Webhook.

Start free