# Webhook signature verification

## Webhook security

Each webhook notification contains the following headers:

| Header parameter            | Description                                                                                                                                                                                                                       | Format |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| `Revolut-Request-Timestamp` | [UNIX timestamp](#timestamp-validation) of the webhook event. Example value: `1715269527223`.                                                                                                                                     | String |
| `Revolut-Signature`         | Signature of the request payload. Contains the current version of the signature-generating algorithm and the hexadecimal-encoded signature. Example value: `v1=d913b5305c6b975e50ed7c5dbb5826d31d39a488f47dc0a935692b7558eb6b23`. | String |

### Webhook signing secret

To ensure that a webhook request originates from Revolut and not a third party, we recommend that you [verify the request's signature](#verify-the-payload-signature) using a signing secret.

Revolut uses the HMAC SHA-256 algorithm to sign its webhooks.
The secret is generated on webhook creation.
After you've created a webhook, you can retrieve the signing secret when you retrieve a specific webhook's details.

### Timestamp validation

You can mitigate replay attacks in your webhooks by applying timestamp validation.

For each webhook event, Revolut sends a `Revolut-Request-Timestamp` header with the exact date-time when it was delivered.

To validate the event, make sure that the `Revolut-Request-Timestamp` date-time is within a 5-minute time tolerance of the current universal time (UTC).

## Verify the payload signature

Follow these steps to verify the signature for the webhook's request payload.

### 1. Prepare the payload to sign

To compute `payload_to_sign`, concatenate the following data, separating each item with a full stop (`.`):

1. The [version](#webhook-security) of the signature-generating algorithm (`v1`)
1. The [`Revolut-Request-Timestamp` header](#webhook-security)
1. The raw webhook payload without whitespaces

```shell
payload_to_sign = {version}.{Revolut-Request-Timestamp}.{raw-payload}
```

An example of `payload_to_sign` might look like this:

```shell
v1.1715269527223.{"order_id":"19218d6e-5f55-4a0d-b7c5-6e333881c1c9","wallet":"0x96e2B7Bf479f84e7A0a94f0620290B7D3E08f5EF","event":"ORDER_CREATED"}
```

:::warning
The signature is sensitive to any modifications, meaning even a small change in the body will result in a completely different signature.
Therefore, it is crucial not to alter the body, especially before the verification.
:::

### 2. Compute the expected signature

To compute the expected signature, you need to concatenate the version of the signature-generating algorithm (`v1`) with the hash-based message authentication code (HMAC).
Separate them with the equals character (`=`).

To compute the HMAC, use the SHA256 hash function and:

- The [signing secret for the webhook](#webhook-signing-secret) as the key
- The payload to sign (prepared in the [previous step](#1-prepare-the-payload-to-sign)) as the message

You can use the following Python implementation for reference:

```python [Computation of the expected signature in Python] {10}
import hmac
import hashlib

signing_secret = 'wsk_8fT55z3C5hCr41l6B0b057D85s2043x4' #Obtained on webhook creation/details retrieval

raw_payload = '{"order_id":"19218d6e-5f55-4a0d-b7c5-6e333881c1c9","wallet":"0x96e2B7Bf479f84e7A0a94f0620290B7D3E08f5EF","event":"ORDER_CREATED"}'
timestamp = '1715269527223'
payload_to_sign = 'v1.' + timestamp + '.' + raw_payload #Prepared in Step 1

signature = 'v1=' + hmac.new(bytes(signing_secret , 'utf-8'), msg = bytes(payload_to_sign , 'utf-8'), digestmod = hashlib.sha256).hexdigest()

print(signature)
```

### 3. Compare signatures

Once you've computed the expected signature, compare it with the signature obtained in the `Revolut-Signature` header of the webhook notification.

The computed signature must match **exactly** the signature sent in that header.