Webhook alerts
Send alerts to your own endpoint, with HMAC signing, custom headers and delivery IDs for deduplication.
A webhook channel posts alerts to a URL you control, so you can drive your own automation — open a ticket, flip a feature flag, page through a system Glowo does not integrate with directly.
Configuration
| Field | Value | Required |
|---|---|---|
url |
The endpoint to POST to | yes |
secret |
A signing secret; enables X-Glowo-Signature |
no |
headers |
Extra headers to send | no |
The request
POST with Content-Type: application/json.
| Header | Meaning |
|---|---|
X-Glowo-Delivery-Id |
Stable across retries of the same logical delivery |
X-Glowo-Event |
The event name; currently always monitor.status_changed |
X-Glowo-Attempt |
Which attempt this is |
X-Glowo-Signature |
Hex HMAC-SHA-256 of the raw body — only when secret is set |
Body:
{
"event": "monitor.status_changed",
"monitor": { "name": "API", "url": "https://api.example.com/health" },
"previousStatus": "UP",
"newStatus": "DOWN",
"latency": 0,
"statusMessage": "CONNECTION_REFUSED",
"downtimeDurationMs": 0,
"timestamp": "2026-08-01T09:15:00.000Z",
"viewUrl": "https://glowo.dev/app/monitors/..."
}
Deduplicate on the delivery ID
Your receiver must be idempotent. Delivery is at-least-once: a request that times out, or that your endpoint fails to acknowledge, is retried.
X-Glowo-Delivery-Id is stable across every retry of the same logical delivery
— only X-Glowo-Attempt changes. Store seen delivery IDs and ignore repeats.
Without that, a slow response on your side turns one outage into three tickets.
Verifying the signature
When secret is set, X-Glowo-Signature is the hex HMAC-SHA-256 of the raw
request body. Compute it over the bytes you received, before any JSON parsing
and re-serialisation — re-encoding changes the bytes and the signature will not
match.
import { createHmac, timingSafeEqual } from "node:crypto";
const verify = (rawBody, signature, secret) => {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
// Length check first: timingSafeEqual throws on a length mismatch.
return a.length === b.length && timingSafeEqual(a, b);
};
Use a constant-time comparison, not ===.
Delivery behaviour
- 10 second timeout. Respond quickly — acknowledge with a
2xxand do the real work asynchronously. Slow endpoints get retried, which means duplicates. - Redirects are not followed. Give Glowo the final URL.
- The target is re-validated at dispatch time, not only when you save the channel. A URL that resolves to a private, loopback or cloud-metadata address is refused — this closes a DNS-rebinding hole, and it means a webhook pointing into a private network will not work by design.
- Failures are retried and eventually dead-lettered, with a per-channel circuit breaker. See Delivery history.
Custom headers
headers are sent with every request — useful for an API key or a routing
header your receiver expects. They are sanitised before sending, and cannot
override the X-Glowo-* headers above.