> ## Documentation Index
> Fetch the complete documentation index at: https://circle-devdocs-test-ai-codegen-component.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# How-to: Verify Webhook Signatures

Every webhook notification sent by CPN is digitally signed with an asymmetric
key. This guide demonstrates how to use the key and signature to verify that a
webhook notification was sent by Circle. Validating webhooks in this way can
reduce the risk of person-in-the-middle attacks on your subscriber endpoint.

## Steps

Use the following steps to verify the Circle signature on a webhook
notification.

### Step 1: Get the digital signature and ID of the notification

Every webhook notification is digitally signed with an asymmetric key. The
asymmetric key is random for each webhook, so you must perform this full
authentication flow to validate the key. This signature is available in the
header of the message. Each message contains the following headers:

* `X-Circle-Signature`: the digital signature generated by Circle
* `X-Circle-Key-Id`: the public key ID in UUID format

Extract those values from the header of the webhook message.

### Step 2: Get the public key and encryption algorithm

Using the `X-Circle-Key-Id` value, query the
[`/v2/cpn/notifications/publicKey/{keyId}`](/api-reference/cpn/common/get-notification-signature)
endpoint.

```shell Shell theme={null}
curl --request GET \
     --url "https://api.circle.com/v2/cpn/notifications/publicKey/${PUBLIC_KEY_ID}" \
     --header "Accept: application/json" \
     --header "authorization: Bearer ${YOUR_API_KEY}"
```

**Response**

```json JSON theme={null}
{
  "data": {
    "id": "879dc113-5ca4-4ff7-a6b7-54652083fcf8",
    "algorithm": "ECDSA_SHA_256",
    "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESl76SZPBJemW0mJNN4KTvYkLT8bOT4UGhFhzNk3fJqf6iuPlLQLq533FelXwczJbjg2U1PHTvQTK7qOQnDL2Tg==",
    "createDate": "2023-06-28T21:47:35.107250Z"
  }
}
```

<Note>
  **Note:** To avoid making multiple requests to the public key endpoint, you
  should cache the public key associated with a given public key ID.
</Note>

### Step 3: Verify the signature

Use the public key and the specified algorithm from the response in step 2,
along with the `X-Circle-Signature` value, to verify the integrity of the
webhook's payload.

The following Python code demonstrates how to verify the `X-Circle-Signature`
value:

```python Python theme={null}
import base64
import json

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec

# Load the public key from the base64 encoded string
public_key_base64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESl76SZPBJemW0mJNN4KTvYkLT8bOT4UGhFhzNk3fJqf6iuPlLQLq533FelXwczJbjg2U1PHTvQTK7qOQnDL2Tg=="
public_key_bytes = base64.b64decode(public_key_base64)
public_key = serialization.load_der_public_key(public_key_bytes)

# Load the signature you want to verify
signature_base64 = "MEQCIBlJPX7t0FDOcozsRK6qIQwik5Fq6mhAtCSSgIB/yQO7AiB9U5lVpdufKvPhk3cz4TH2f5MP7ArnmPRBmhPztpsIFQ=="
signature_bytes = base64.b64decode(signature_base64)

# Load and format the message you want to verify
message = "{\"subscriptionId\":\"00000000-0000-0000-0000-000000000000\",\"notificationId\":\"00000000-0000-0000-0000-000000000000\",\"notificationType\":\"webhooks.test\",\"notification\":{\"hello\":\"world\"},\"timestamp\":\"2024-01-26T18:22:19.779834211Z\",\"version\":2}"
message_bytes = message.encode(encoding="utf-8")

# Verify the signature
try:
    public_key.verify(
        signature_bytes,
        message_bytes,
        ec.ECDSA(hashes.SHA256())
    )
    print("Signature is valid.")
except InvalidSignature:
    print("Signature is invalid.")
```

<Tip>
  **Tip:** Ensure that the webhook payload that you input in the `message` field
  is a properly formatted JSON string. Invalid JSON causes verification failure.
</Tip>
