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.