How to validate webhook calls

How to validate webhook calls by using the signingSecret value.

Whenever a webhook is called, a X-Satws-Signature header is sent to help verify it. Syntage generates a signature using a hash-based message authentication code (HMAC) with SHA-256 using the signingSecret as key. To verify the hash you can do the following steps:

Step 1

Extract the timestamp and signature from the headerSplit the header, using the , character as the separator. The value for the prefix t corresponds to the timestamp and s corresponds to the signature.

Step 2:

Prepare the signed_payload stringThe signed_payload string is created by concatenating (without spaces): - The timestamp (as a string in Unix format) example: "1656569160" - The character . (dot) - The actual JSON payload (that is, the request body)

Step 3:

Determine the expected signatureCompute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the signed_payload string as the message.

Step 4:

Compare the signaturesCompare the signature (or signatures) in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures.

Here's a code example using PHP with a constant-time string:

    function splitString(string $separator, $stringValue): array
      return explode($separator, $stringValue);

    ## X-Satws-Signature
    $xSatSignatureHeader = 't=1656569160,s=527124c570b27b3f268777b2ba96a9bbdc4b0ecde2885f688beda528f39c4e23';

    list($timestamp, $signature) = array_map(function($signatureParam){
    return splitString('=', $signatureParam)[1];
    }, splitString(',', $xSatSignatureHeader));

    $bodyPayload = '{
    "@context": "/contexts/Event",
    "@id": "/events/88a88df8-5c55-44a4-a222-ef9999c999",
    "@type": "Event",
    "id": "88a88df8-5c55-44a4-a222-ef9999c999",
    "type": "credential.updated",
    "source": None,
    "createdAt": "2022-06-29 20:14:09",
    "updatedAt": "2022-06-29 20:14:09"
    $signingSecret = '320639996d9eee9178bf89d26cdbc23d';

    $hash = hash_hmac('sha256', sprintf('%d.%s', $timestamp, $bodyPayload), $signingSecret);

    print strcmp($hash, $signature);

What’s Next