Webhook Signature Verification
Verify webhook signatures to ensure payloads are authentic and haven't been tampered with.
We Sign the Payload
Using your webhook secret, we compute an HMAC-SHA256 hash of the raw request body.
Signature in Header
The signature is sent in the X-Regen-Signature header.
You Verify
Compute the same hash on your end and compare. If they match, the payload is authentic.
| Header | Description | Example |
|---|---|---|
X-Regen-Signature | HMAC-SHA256 signature | sha256=a1b2c3d4... |
X-Regen-Timestamp | Unix timestamp when sent | 1703001234 |
X-Regen-Event | Event type | order.created |
X-Regen-Delivery-ID | Unique delivery identifier | del_abc123... |
X-Regen-Retry-Count | Number of retry attempts | 0 |
import crypto from 'crypto';
const WEBHOOK_SECRET = process.env.REGEN_WEBHOOK_SECRET;
export function verifyWebhookSignature(
payload: string,
signature: string,
timestamp: string
): boolean {
// Check timestamp is within 5 minutes (prevent replay attacks)
const currentTime = Math.floor(Date.now() / 1000);
const webhookTime = parseInt(timestamp, 10);
if (Math.abs(currentTime - webhookTime) > 300) {
console.error('Webhook timestamp too old');
return false;
}
// Create the signed payload string
const signedPayload = `${timestamp}.${payload}`;
// Compute expected signature
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(signedPayload)
.digest('hex');
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Usage in your webhook handler
export async function POST(request: Request) {
const payload = await request.text();
const signature = request.headers.get('X-Regen-Signature');
const timestamp = request.headers.get('X-Regen-Timestamp');
if (!verifyWebhookSignature(payload, signature, timestamp)) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(payload);
// Process the verified event...
return new Response('OK', { status: 200 });
}The X-Regen-Timestamp header contains the Unix timestamp of when the webhook was sent. Always verify this timestamp to prevent replay attacks.
Recommended tolerance:
- • 5 minutes (300 seconds) - Recommended default
- • 1 minute (60 seconds) - High security environments
- • 15 minutes (900 seconds) - If your server clock may drift
Signature mismatch
Ensure you're using the raw request body, not a parsed/modified version. JSON parsing can alter whitespace and key ordering.
Wrong webhook secret
Each webhook endpoint has its own secret. Make sure you're using the correct secret for the specific endpoint receiving the webhook.
Encoding issues
Ensure the payload is treated as UTF-8. Different encodings will produce different hash values.
Debug tip
Log both the expected and received signatures (without exposing the secret) to identify where the mismatch occurs.