Skip to main content

Request validation

Webhooks requests should be validated using the X-Webhook-Timestamp and X-Webhook-Signature headers and a shared secret key.

To verify the signature, create an SHA-256 hash of the request body with the X-Webhook-Timestamp header value appended to it using the shared secret key for the webhook and convert it to base64. If the calculated hash matches the value of the X-Webhook-Signature the request can be assumed to be valid.

NodeJS example

// Create digest with payload + hmac secret
const timestamp = request.get('X-Webhook-Timestamp')
const hashPayload = request.rawBody + timestamp
const hmac = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
const digest = hmac.update(hashPayload).digest('base64')

// Get hash sent by the provider
const providerSig = request.get('X-Webhook-Signature')

// Compare digest signature with signature sent by provider
if (providerSig.length == digest.length && crypto.timingSafeEqual(digest, providerSig)) {
// Webhook Authenticated!
console.log(request.json())
}

PHP example

define('API_SECRET_KEY', 'my_webhook_api_secret');

function verify_webhook($data, $hmac_header)
{
# Calculate HMAC
$calculated_hmac = base64_encode(hash_hmac('sha256', $data, API_SECRET_KEY, true));
return hash_equals($hmac_header, $calculated_hmac);
}

# Extract the signature header
$hmac_header = $_SERVER['X-Webhook-Signature'];

# Combine the timestamp header value and raw body
$timestamp = $_SERVER['X-Webhook-Timestamp'];
$data = file_get_contents('php://input') + $timestamp;

# Compare HMACs
$verified = verify_webhook($data, $hmac_header);

if ($verified) {
# Do something with the webhook data!
}