Webhooks
Get alerts as signed JSON POST requests to any URL you control.
Overview
Webhooks let your own systems react to monitoring events. When a monitor goes down, comes back up, or its SSL certificate is about to expire, Notifier sends an HTTP POST with a JSON body to the URL you choose. Use them to open tickets, trigger runbooks, post to chat tools we don't integrate with yet, or feed your own dashboards.
Adding a Webhook
- Go to Integrations in the monitoring dashboard.
- In the Webhooks section, click Add webhook.
- Enter a name and the URL that should receive events (http or https).
- Choose the events to send: monitor down, monitor up, SSL expiring.
- Under Which monitors?, choose All monitors (including ones you add later) or Only selected monitors and tick them. You can also add or remove a webhook from a monitor in that monitor's Notifications settings.
- Optionally add up to 10 custom headers, such as an
Authorizationtoken your endpoint expects. - Save, copy the signing secret, and click Send test to check your endpoint.
Pushover and ntfy
To get alerts as push notifications on your phone, pick a ready-made Format when you add the webhook. Notifier then sends the request each app expects, with a readable title, the monitor's address and a link to the incident. You don't need to build anything.
Pushover
- Copy your user key from the top of your pushover.net dashboard.
- On pushover.net, choose Create an Application/API Token, name it Notifier, and copy its API token.
- In Notifier, add a webhook, choose the Pushover format and paste both. There's no URL to enter.
Down alerts are sent with high priority, so they bypass quiet hours. Recovery, SSL and test alerts use normal priority.
ntfy
- Pick a hard to guess topic name and subscribe to it in the ntfy app.
- In Notifier, add a webhook, choose the ntfy format and enter the topic URL, for example
https://ntfy.sh/your-topic. Self-hosted ntfy servers work too. - For a protected topic, add a custom header named
Authorizationwith the valueBearer tk_...(your ntfy access token).
Down alerts use ntfy's highest priority, SSL warnings high priority, and recoveries and tests the default.
Events
monitor.down: a monitor failed its checks and an incident was opened.monitor.up: the monitor recovered and the incident was resolved.ssl.expiring: the monitor's SSL certificate crossed a warning threshold (30, 14, 7 or 1 days before expiry).test: sent only when you click Send test.
Payload
Every request has Content-Type: application/json. A monitor.down event looks like this:
{
"event_id": "9771f505-ce7c-4f85-9d02-c45a43cea464",
"event": "monitor.down",
"occurred_at": "2026-10-21T14:03:12Z",
"monitor": {
"id": 1155,
"name": "My website",
"type": "http",
"url": "https://example.com",
"hostname": null,
"port": null,
"dashboard_url": "https://notifier.so/new/monitoring"
},
"incident": {
"id": 48211,
"status": "ongoing",
"started_at": "2026-10-21T14:03:12Z",
"ended_at": null,
"duration_seconds": null,
"url": "https://notifier.so/new/monitoring/incidents/incident?id=48211"
}
}
monitor.up has the same shape with "status": "resolved", ended_at and duration_seconds filled in. ssl.expiring has an ssl object instead of incident:
"ssl": {
"expires_at": "2026-11-04T23:59:59Z",
"days_remaining": 14,
"issuer": "Let's Encrypt"
}
Times are UTC in ISO 8601. New fields may be added over time, so ignore fields you don't recognize.
Request Headers
X-Notifier-Event: the event type, for examplemonitor.down.X-Notifier-Delivery: the event ID. It stays the same on every retry, so use it to ignore duplicates.X-Notifier-Attempt: the attempt number, starting at 1.X-Notifier-Signature: the signature described below.User-Agent:Notifier-Webhooks/1.0.
Verifying Signatures
Each request is signed with your webhook's secret (it starts with whsec_). The header looks like t=1761055392,v1=5257a8..., where v1 is the hex HMAC SHA256 of the timestamp, a period, and the raw request body. To verify:
- Read the raw body before parsing the JSON.
- Compute HMAC SHA256 with your secret over
"{t}.{raw body}". - Compare it to
v1with a constant time comparison. - Reject requests whose timestamp is more than 5 minutes old to block replays.
Python
import hashlib, hmac, time
def verify(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool:
parts = dict(item.split("=", 1) for item in header.split(","))
t = int(parts["t"])
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Node.js
const crypto = require("crypto");
function verify(secret, header, rawBody, tolerance = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (Math.abs(Date.now() / 1000 - t) > tolerance) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 || "");
return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), given);
}
You can reveal or rotate the secret from the webhook's settings. Rotating takes effect immediately.
Responses and Retries
- Any 2xx response within 10 seconds counts as delivered. Respond quickly and do slow work in the background.
- Redirects are not followed. Use the final URL.
- Failed deliveries are retried up to 5 attempts in total: right away, then after 30 seconds, 2 minutes, 10 minutes and 30 minutes. Every attempt carries the same event ID.
- If an endpoint fails every delivery for 48 hours, Notifier pauses it and emails the team owner. Your other alert channels keep working. Turn the webhook back on once it's fixed.
- Each endpoint accepts up to 60 deliveries per minute.
Delivery Log
Open a webhook's deliveries to see every attempt from the last 30 days: time, event, attempt number, status code, response time, error, the payload we sent and the start of your response. Click Resend to send an event again with the same event ID.
Security Notes
- Webhook URLs must resolve to public internet addresses. Private, loopback and cloud metadata addresses are rejected.
- Credentials inside the URL (
https://user:pass@...) are not allowed. Use a custom header instead. - Always verify the signature before trusting a request.
Plan Availability
| Plan | Webhooks |
|---|---|
| Free | Not available |
| Solo | Available |
| Team | Available |
| Enterprise | Available |