Skip to main content

Verifying requests

To ensure that the requests you're getting at your webhook endpoint are actually coming from Kavitro, Kavitro sends an X-Kavitro-Signature header containing an HMAC-SHA256 of the request body, keyed with your endpoint's secret.

Before you can verify signatures, you need to retrieve your endpoint's secret from your webhook settings under Settings > Integrations > Webhooks. Select the endpoint you want to obtain the secret for, then click the "Show" button.

Kavitro generates a unique secret for each endpoint. If you use multiple endpoints, you must obtain a secret for each one you want to verify signatures on.

Verifying signatures

Compute an HMAC-SHA256 of the raw request body using your endpoint's secret as the key, then compare the result with the value of the X-Kavitro-Signature header. If the two match, the request came from Kavitro - or from someone else who knows your secret, which is why it must be kept safe.

If they do not match, the request may have been tampered with in transit, or someone may be spoofing webhook notifications to your endpoint. Reject it with a 4xx status and do not process the payload.

caution

Two details decide whether this works:

  • Sign the raw body, exactly as received. Do not decode the JSON and re-encode it - key order, escaping and whitespace will not survive the round trip, and the signature will never match.
  • Compare with a timing-safe function such as PHP's hash_equals(). A plain === comparison leaks information about the expected value.

Here's a quick example written in PHP:

$secret = 'xxxxxxxxxx';
$rawBody = file_get_contents('php://input');

$expected = hash_hmac('sha256', $rawBody, $secret);
$received = $_SERVER['HTTP_X_KAVITRO_SIGNATURE'] ?? '';

if (! hash_equals($expected, $received)) {
http_response_code(403);
exit;
}

$events = json_decode($rawBody, true);