Platform
Alerts and webhooks
Alert rules, where they’re delivered, the webhook payload and how to verify its signature.
Alerts watch your traffic so you don’t have to. Every new site starts with three rules turned on: a traffic spike, the first conversion of the day, and an error spike. There is no limit on the number of rules.
Alert rules
| Kind | Fires when | Options |
|---|---|---|
traffic_spike | Visitors are well above the usual level for this hour | percent, minVolume |
traffic_drop | Visitors are well below the usual level | percent, minVolume |
goal_conversion | A goal converts (optionally only the first of the day) | goalId, firstOfDay |
event | A matching event arrives: “a visitor from news.ycombinator.com on /pricing”, “a purchase over $500” | event, path, referrer, channel, country, minRevenue |
error_spike | JavaScript errors are well above usual | percent, minVolume |
online_threshold | Online now crosses a number | threshold |
Each rule has a cooldown (30 minutes by default, cooldownMinutes) so a busy day doesn’t flood you, and a Send test button.
Delivery
- Always: the notification center in the dashboard, plus browser notifications if you allow them.
- Optionally: a webhook URL per rule. Slack and Discord webhook URLs are detected and get a formatted message; any other URL gets the JSON below.
- Webhook URLs must point to a public address; private and loopback addresses are refused.
Webhook payload
{
"type": "alert.triggered", // or "alert.test"
"alert": { "id": "alr_…", "name": "Traffic spike", "kind": "traffic_spike" },
"site": { "id": "SITE_ID", "name": "Acme", "domain": "acme.com" },
"title": "Traffic spike on acme.com",
"message": "412 visitors in the last hour, 3.1× the usual.",
"data": { … }, // kind-specific numbers
"url": "https://your-globatics/app/SITE_ID", // when GLOBATICS_PUBLIC_URL is set
"ts": 1792631000000,
"triggeredAt": "2026-10-21T14:23:20.000Z"
}| Header | Value |
|---|---|
X-Globatics-Signature | hex(HMAC-SHA256(raw body, signing secret)) |
X-Globatics-Event | alert.triggered or alert.test |
X-Globatics-Delivery | Unique delivery id (same on retries) |
Verify the signature
Each site has its own signing secret (it starts with whsec_), shown in the alerts settings and returned by GET /api/sites/:siteId/alerts. Compute the HMAC over the raw request body, before parsing it, and compare in constant time.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyGlobatics(rawBody: string, signature: string | null, secret: string): boolean {
if (!signature) return false;
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
// Next.js route handler
export async function POST(req: Request) {
const raw = await req.text();
if (!verifyGlobatics(raw, req.headers.get("x-globatics-signature"), process.env.GLOBATICS_WHSEC!)) {
return new Response("Bad signature", { status: 401 });
}
const event = JSON.parse(raw);
// …
return new Response(null, { status: 204 });
}Self-hosting
Signing secrets are derived from the server secret: GLOBATICS_SECRET when it is set, otherwise a random secret generated on first start and stored in the database. Changing it changes every site’s signing secret.
Retries and logs
- A delivery succeeds on any 2xx response. Timeouts, network errors, 408, 429 and 5xx responses are retried 3 times with backoff (4 attempts in all).
- Redirects aren’t followed. Answer quickly and do the work in the background.
- Every delivery is logged with its payload, status, response code and attempts:
GET /api/sites/:siteId/alerts/deliveries.