Verify Webhook Signatures
Validate Manifest AI webhook timestamps and HMAC signatures with replay protection and constant-time comparison.
Read the unmodified request body, reject timestamps older than five minutes, calculate HMAC-SHA256 over timestamp + '.' + raw body, and compare the supplied signature in constant time.
Required headers
Keep these values with the raw body until verification completes.
- Manifest-Event-Id
- Stable delivery identifier for deduplication.
- Manifest-Timestamp
- Unix timestamp included in the signed content.
- Manifest-Signature
- Versioned HMAC-SHA256 signature.
Node.js verification
Verify before JSON parsing or business processing.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!Number.isFinite(age) || age > 300) throw new Error("Stale event");
const expected = createHmac("sha256", webhookSecret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const supplied = signature.replace(/^v1=/, "");
if (expected.length !== supplied.length ||
!timingSafeEqual(Buffer.from(expected), Buffer.from(supplied))) {
throw new Error("Invalid signature");
}Replay safety
Signature validity alone does not prevent an old valid event from being replayed.
- Enforce a five-minute timestamp tolerance.
- Store processed event IDs for at least the retry window.
- Return success for an already-processed valid event.
- Never log signing secrets or full customer payloads.