Skip to content
English
  • There are no suggestions because the search field is empty.

Webhook Security

Webhook Security
All webhook payloads are signed using HMAC-SHA256 to verify authenticity.


Headers

Header Description
X-Claudio-Signature HMAC-SHA256 signature (hex-encoded)
X-Claudio-Timestamp Unix timestamp of the request

Verifying Signatures

  1. Retrieve the raw JSON request body

  2. Compute HMAC-SHA256(body, your_webhook_secret)

  3. Compare the result with the X-Claudio-Signature header

Important: Use a constant-time comparison function (e.g., hash_equals() in PHP, hmac.compare_digest() in Python) to prevent timing attacks.


Example (PHP)

$payload = file_get_contents('php://input');
$signature = hash_hmac('sha256', $payload, $webhookSecret);

if (!hash_equals($signature, $_SERVER['HTTP_X_CLAUDIO_SIGNATURE'])) {
    http_response_code(401);
    exit('Invalid signature');
}

 

Example (Python)

import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected_signature, signature)

# In your webhook handler
if not verify_webhook(request.body, request.headers['X-Claudio-Signature'], webhook_secret):
    return Response(status=401)

 

Example (Node.js)

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
    const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(payload)
        .digest('hex');
    
    return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expectedSignature)
    );
}
// In your webhook handler
if (!verifyWebhook(req.body, req.headers['x-claudio-signature'], webhookSecret)) {
    return res.status(401).send('Invalid signature');
}