Skip to content

Signature Verification

Every webhook delivery includes an X-Signature-SHA256 header containing an HMAC-SHA256 signature. You should verify this signature to ensure the request genuinely came from Invoro and hasn’t been tampered with.

  1. Invoro computes HMAC-SHA256 of the JSON request body using your webhook secret as the key
  2. The result is prefixed with sha256= and sent in the X-Signature-SHA256 header
  3. Your server computes the same HMAC and compares it to the header value
import crypto from 'node:crypto';
function verifyWebhookSignature(payload, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-signature-sha256'];
const event = req.headers['x-webhook-event'];
if (!verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body);
console.log(`Received ${event}:`, payload);
// Process the event asynchronously
res.status(200).send('OK');
});
function verifyWebhookSignature(string $payload, string $signature, string $secret): bool
{
$expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expected, $signature);
}
// Usage
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE_SHA256'] ?? '';
$secret = getenv('WEBHOOK_SECRET');
if (!verifyWebhookSignature($payload, $signature, $secret)) {
http_response_code(401);
exit('Invalid signature');
}
$event = $_SERVER['HTTP_X_WEBHOOK_EVENT'] ?? '';
$data = json_decode($payload, true);
// Process the event
http_response_code(200);
echo 'OK';
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# Flask example
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Signature-SHA256', '')
event = request.headers.get('X-Webhook-Event', '')
if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
payload = request.get_json()
print(f'Received {event}: {payload}')
# Process the event asynchronously
return 'OK', 200

You can verify a signature using the command line:

Terminal window
echo -n '{"event":"test","message":"This is a test"}' | \
openssl dgst -sha256 -hmac "your-webhook-secret"

The output should match the value after sha256= in the X-Signature-SHA256 header.