notlogs rocket
notlogs

Alerts

Webhook Delivery & Security

Every webhook notification dispatched by NotLogs includes cryptographic HMAC-SHA256 signatures to verify authenticity.

Webhook Request Headers

HeaderDescription
x-oml-event-idUnique UUID for each dispatched webhook event
x-oml-signatureHMAC-SHA256 hex digest of ${timestamp}.${body}
x-oml-timestampUnix 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
typescript
import 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": { ... }
  }'