Skip to content
Glowo
Esc
navigateopen⌘Jpreview
On this page

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 2xx and 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.

Was this page helpful?