Webhooks & EventsReference
Verifying Webhook Signatures
Authenticate deliveries with the raw request body, HMAC-SHA256, and constant-time comparison.
9 min readUpdated 25 Sept 20264 guides in this section
Treat every webhook as untrusted input. Verify the signature before parsing or acting on the payload, reject stale timestamps, and use the raw request bytes for verification.
Delivery headers
Express verification example
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const secret = process.env.OP26X_WEBHOOK_SECRET;
app.post('/webhooks/op26x', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.get('x-op26x-signature');
const timestamp = req.get('x-op26x-timestamp');
const deliveryId = req.get('x-op26x-delivery');
if (!signature || !timestamp || !deliveryId || !secret) {
return res.sendStatus(400);
}
const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!Number.isFinite(ageSeconds) || ageSeconds > 300) {
return res.sendStatus(401);
}
const expected = crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
const expectedBuffer = Buffer.from(expected, 'utf8');
const receivedBuffer = Buffer.from(signature, 'utf8');
if (
expectedBuffer.length !== receivedBuffer.length ||
!crypto.timingSafeEqual(expectedBuffer, receivedBuffer)
) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString('utf8'));
enqueueForProcessing(deliveryId, event);
return res.sendStatus(202);
});Response behavior
- Return a 2xx response only after the delivery has been accepted durably.
- Prefer queueing the work and acknowledging quickly rather than blocking the request.
- Return a 400 response for malformed requests and a 401 response for failed authentication.
- Deduplicate by delivery ID because webhook delivery can be at-least-once.
- Never log signing secrets, authorization headers, or full sensitive payloads.
Secret rotation
- 01
Create the replacement secret in the workspace
Store the new value in the integration secret manager rather than source code.
- 02
Accept both values during rollout
Select the secret by the configured key identifier or deployment configuration.
- 03
Verify the new secret
Send a signed test delivery and confirm the new path succeeds.
- 04
Revoke the old secret
Remove the previous credential after all senders use the replacement.
Need implementation help?
Search the help center or talk with the OP26X telephony team.