Verify Webhooks Requests
Learn how to use the signing secret to verify your webhooks.
To ensure that webhook requests are authentic and coming from Reloop, verify the signature included in the request headers. This prevents unauthorized requests from reaching your application logic.
Headers
Every delivery includes:
| Header | Description |
|---|---|
Reloop-Id | Event id (whev_…), same as body id |
Reloop-Timestamp | Unix timestamp (seconds) used in the signature |
Reloop-Signature | t=<timestamp>,v1=<hex hmac> |
Signature algorithm
- Read the raw request body bytes (do not re-serialize JSON).
- Read
Reloop-Timestamp(or parset=fromReloop-Signature). - Compute HMAC-SHA256 over the string
`${timestamp}.${rawBody}`using your endpoint secret (whsec_…). - Compare the hex digest to the
v1=value with a constant-time compare. - Reject if the timestamp is older than ~5 minutes (replay protection).
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyReloopWebhook(input: {
secret: string;
rawBody: string;
signatureHeader: string;
toleranceSeconds?: number;
}): boolean {
const parts = Object.fromEntries(
input.signatureHeader.split(",").map((p) => {
const [k, ...rest] = p.trim().split("=");
return [k, rest.join("=")];
}),
);
const timestamp = Number(parts.t);
const v1 = parts.v1;
if (!Number.isFinite(timestamp) || !v1) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > (input.toleranceSeconds ?? 300)) return false;
const expected = createHmac("sha256", input.secret)
.update(`${timestamp}.${input.rawBody}`)
.digest("hex");
try {
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
} catch {
return false;
}
}
Payload shape
{
"id": "whev_01h…",
"type": "email.delivered",
"created_at": "2026-07-22T12:00:00.000Z",
"data": {
"email_id": "em_…",
"from": "hello@example.com",
"to": ["user@example.com"],
"subject": "Welcome",
"status": "delivered"
}
}
Why verify webhooks?
Verification is critical because it allows you to:
- Confirm the request was signed with your endpoint secret
- Reject forged events from third parties
- Detect and drop replayed requests outside the time window
Was this page helpful?