Browse documentation
Webhooks

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.