Verify signatures
Verify webhook headers, timestamps, and raw-body signatures and reject replays.
Jetrepo signs each request with the Standard Webhooks format. The whsec_… secret is returned only when a subscription is created.
Verification order
- Capture the raw body bytes before framework JSON parsing changes them.
- Read
webhook-id,webhook-timestamp, andwebhook-signature. - Pass the raw body, headers, and secret to a maintained Standard Webhooks library.
- Apply your timestamp tolerance and reject stale requests with non-2xx.
- Deduplicate by
webhook-id, then parse and enqueue the payload.
// Adapt header and raw-body access to your HTTP framework.
const headers = {
'webhook-id': request.headers.get('webhook-id')!,
'webhook-timestamp': request.headers.get('webhook-timestamp')!,
'webhook-signature': request.headers.get('webhook-signature')!,
};
verifyWithMaintainedStandardWebhooksLibrary({
rawBody,
headers,
secret: process.env.JETREPO_WEBHOOK_SECRET!,
});
if (await alreadyProcessed(headers['webhook-id'])) return response(204);
await durableQueue.add(JSON.parse(rawBody.toString('utf8')));
return response(202);
Use the API exposed by your maintained library; do not hand-roll HMAC comparison. Jetrepo signs the stable message ID, the send timestamp, and the exact JSON.stringify(payload) body. Across retries, message ID and payload stay the same while timestamp and signature change.
The receiver needs no CMS permission. Creating the subscription requires Backend-scoped webhook.create. Store the whsec_ plus base64 secret in a secret manager and never log it.
Choose a replay retention period that covers at least the webhook retry horizon plus your receiver’s operational risk. Jetrepo does not set a numeric tolerance for your verification library.