> For the complete documentation index, see [llms.txt](https://docs.bird.com/api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bird.com/api/notifications-api/api-reference/webhook-subscriptions/verifying-a-webhook-subscription.md).

# Verifying a webhook subscription

If you have created your [webhook subscription](/api/notifications-api/api-reference/webhook-subscriptions/create-a-webhook-subscription.md) using a signingKey you can validate the authenticity of the webhook by validating the request signature sent in the request header.

From the incoming webhook, parse the following request headers:

`messagebird-signature`

`messagebird-request-timestamp`

In addition, parse the request `URL` and the request `Body`.

To calculate the request signature:

1. Base64 decode the `messagebird-signature` header;
2. Create a SHA256 hash checksum of the request `body` as a binary result;
3. Join the request timestamp (`messagebird-request-timestamp` header) with the request URL and `request body` checksum computed in step 2, separated by a new line (`\n`);
4. Calculate HMACSHA256 using the signing key as the secret and the joined payload from step 3 to calculate the signature;
5. Compare the output of Step 4 with the signature from Step 1. The code snippets below illustrate the intended process. We recommend tailoring these examples to fit your preferred programming language, codebase, or framework.

<figure><img src="/files/nJ0GsujcfRD2BBiA8CpE" alt=""><figcaption><p>Example of header content used for validation. You can see the messagebird-signature and the messagebird-request-timestamp, which are used to validate the request. The event reference is the messagebird-request-id, which can be used for debugging failures using Webhook subscription logs. For more information regarding logs, refer to <a data-mention href="/pages/9m0rJnR7aMCDh0ScHLLA">/pages/9m0rJnR7aMCDh0ScHLLA</a>.</p></figcaption></figure>

**Examples**

The following code snippets are provided to illustrate the intended process. We recommend adapting them to your preferred programming language, codebase, or framework to ensure compatibility.

{% tabs %}
{% tab title="PHP" %}

```
<?php

function verifyWebhookSignature(
    string $headerSignature,
    string $headerTimestamp,
    string $receivedBody,
    string $requestedUrl,
    string $signingKey
): bool {
    $receivedDecodedSignature = base64_decode($headerSignature);

    $bodyHash = hash('sha256', $receivedBody, true);
    $computedSignature = hash_hmac(
        'sha256',
        sprintf("%s\n%s\n%s", $headerTimestamp, $requestedUrl, $bodyHash),
        $signingKey,
        true
    );

    return hash_equals($computedSignature, $receivedDecodedSignature);
}

// Example usage
$isVerified = verifyWebhookSignature(
    $_SERVER['HTTP_MESSAGEBIRD_SIGNATURE'],
    $_SERVER['HTTP_MESSAGEBIRD_REQUEST_TIMESTAMP'],
    file_get_contents('php://input'),
    'https://domain.com/webhook/bird',
    'secureSigningKey'
);
```

{% endtab %}

{% tab title="Golang" %}

```
package main

import (
    "bytes"
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "os"
)

func main() {
    var key string
    var signatureHeader string
    var timestampHeader string
    var url string
    var body []byte

    // Example usage
    match, err := checkSignature(key, signatureHeader, timestampHeader, url, body)
    if err != nil {
       // Error out
       fmt.Printf("something went wrong: %v", err)
       os.Exit(1)
    }

    if !match {
       // Signature doesn't match
       fmt.Println("Signatures don't match!")
    } else {
       // Signature matches
       fmt.Println("Signatures match!")
    }
}

// checkSignature verifies the Bird style HMAC-SHA256 signature for the given data.
func checkSignature(signingKey, signatureHeader, timestampHeader, requestURL string, data []byte) (bool, error) {
    // Step 1: Decode the signature from the header
    actualSignature, err := base64.StdEncoding.DecodeString(signatureHeader)
    if err != nil {
       return false, err
    }

    // Step 2 - 4: Calculate the expected signature
    expectedSignature, err := signSha256(signingKey, timestampHeader, requestURL, data)
    if err != nil {
       return false, err
    }

    // Step 5: Compare the expected signature with the actual signature
    return hmac.Equal(expectedSignature, actualSignature), nil
}

// signSha256 returns the Bird style HMAC-SHA1 signature for the given data.
func signSha256(signingKey, timestamp, requestURL string, data []byte) ([]byte, error) {
    // Step 2: Calculate the SHA256 hash for the given data
    bh := sha256.Sum256(data)

    // Step 3: Concatenate the timestamp, request URL, and the SHA256 hash, separated by a newline character
    var m bytes.Buffer
    _, err := fmt.Fprintf(&m, "%s\n%s\n%s", timestamp, requestURL, bh)
    if err != nil {
       return []byte{}, err
    }

    // Step 4: Calculate the HMAC-SHA256 hash for the concatenated string using the signing key
    mac := hmac.New(sha256.New, []byte(signingKey))
    if _, err := mac.Write(m.Bytes()); err != nil {
       return []byte{}, err
    }
    return mac.Sum(nil), nil
}
```

{% endtab %}

{% tab title="Python" %}

```
import hashlib
import hmac
import base64
import time

class SignedRequest:
    def __init__(self, requestSignature, requestTimestamp, requestBody, requestUrl):
        self._requestSignature = requestSignature
        self._requestTimestamp = str(requestTimestamp)
        self._requestBody = requestBody
        self._requestUrl = requestUrl

    def verify(self, signing_key):
        payload = self._build_payload()
        expected_signature = base64.b64decode(self._requestSignature)
        calculated_signature = hmac.new(signing_key.encode('latin-1'), payload.encode('latin-1'),
                                        hashlib.sha256).digest()
        return expected_signature == calculated_signature

    def is_recent(self, offset=10):
        return int(time.time()) - int(self._requestTimestamp) < offset

    def _build_payload(self):
        checksum_body = hashlib.sha256(self._requestBody.encode('latin-1')).digest()
        str_checksum_body = checksum_body.decode('latin-1')
        parts = [self._requestTimestamp, self._requestUrl, str_checksum_body]
        return "\n".join(parts)
```

{% endtab %}
{% endtabs %}
