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.
How It Works
Section titled “How It Works”- Invoro computes
HMAC-SHA256of the JSON request body using your webhook secret as the key - The result is prefixed with
sha256=and sent in theX-Signature-SHA256header - Your server computes the same HMAC and compares it to the header value
Verification Examples
Section titled “Verification Examples”Node.js
Section titled “Node.js”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 exampleapp.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 eventhttp_response_code(200);echo 'OK';Python
Section titled “Python”import hmacimport 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 examplefrom 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', 200Important Notes
Section titled “Important Notes”Verifying Manually
Section titled “Verifying Manually”You can verify a signature using the command line:
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.