OdyhookSign in

Outbound HMAC

Each delivery Odyhook forwards is signed with that destination's secret. Two headers carry the signature:

  • X-Odyhook-Signature: v1=<hex>
  • X-Odyhook-Timestamp: <unix-seconds>

The signed value is `${timestamp}.${rawBody}`, run through HMAC-SHA256 with the destination secret and hex-encoded. Recompute it on your side and compare in constant time.

import crypto from "node:crypto";

function verify(rawBody, sigHeader, tsHeader, secret) {
const sig = sigHeader.replace(/^v1=/, "");
const expected = crypto
  .createHmac("sha256", secret)
  .update(`${tsHeader}.${rawBody}`, "utf8")
  .digest("hex");
const a = Buffer.from(sig, "hex");
const b = Buffer.from(expected, "hex");
// Guard the length first: timingSafeEqual throws on unequal-length buffers.
return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Reject requests whose X-Odyhook-Timestamp is too old. Comparing the signature alone doesn't stop replay — a stale-timestamp check does.