Alerts
Webhook Delivery & Security
Every webhook notification dispatched by NotLogs includes cryptographic HMAC-SHA256 signatures to verify authenticity.
Webhook Request Headers
| Header | Description |
|---|---|
| x-oml-event-id | Unique UUID for each dispatched webhook event |
| x-oml-signature | HMAC-SHA256 hex digest of ${timestamp}.${body} |
| x-oml-timestamp | Unix millisecond timestamp when the alert triggered |
Payload Structure
{
"eventId": "e932ba81-df92-4f11-8255-a2202685718a",
"type": "alert.triggered",
"alertId": "c92881a2-7dc2-4821-b3b4-1c9f80a3c4de",
"alertName": "Production Database Timeouts",
"matchedCount": 4,
"sampleLog": {
"type": "error",
"message": "Database query timed out after 5000ms",
"service": "orders-api",
"subsystem": "db",
"importance": 4,
"environment": "production",
"timestamp": 1756402800
},
"triggeredAt": "2026-08-28T22:30:00.000Z"
}Verifying Signatures in Node.js
pages/api/webhook.ts
typescriptimport crypto from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const signature = req.headers["x-oml-signature"] as string;
const timestamp = req.headers["x-oml-timestamp"] as string;
const secret = process.env.WEBHOOK_SIGNING_SECRET!;
// 1. Prevent replay attacks (reject if older than 5 minutes)
if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) {
return res.status(400).json({ error: "Timestamp expired" });
}
// 2. Compute expected HMAC
const rawBody = typeof req.body === "string" ? req.body : JSON.stringify(req.body);
const signedPayload = `${timestamp}.${rawBody}`;
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(signedPayload, "utf8")
.digest("hex");
// 3. Constant-time timing-safe comparison
const isValid = crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expectedSignature, "hex")
);
if (!isValid) {
return res.status(401).json({ error: "Invalid signature" });
}
// Process verified alert
const alertEvent = req.body;
console.log("Verified Alert Event:", alertEvent.alertName);
return res.status(200).json({ received: true });
}Verification Helper Endpoint
You can also verify signatures directly by calling our backend verification endpoint:
curl -X POST https://api.api.notlogs.site/api/v1/alert/verify-webhook \
-H "Content-Type: application/json" \
-d '{
"signature": "c8f8b7...",
"timestamp": "1756402800000",
"body": { ... }
}'