> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fintoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Validate the signature of your Webhook Endpoint

By the end of this guide you can verify that every webhook event came from Fintoc by checking the `Fintoc-Signature` header.

Fintoc signs every event sent to your webhook endpoints with the `Fintoc-Signature` header. This header lets you verify that Fintoc, not a third party, sent each event.

Fintoc generates a secret when you register a [webhook endpoint](/reference/main-resources/webhook-endpoints/webhook-endpoints-create). This secret is necessary to validate the Fintoc signature of every event.

## Verify that Fintoc sent the event

Each event sent by Fintoc includes the `Fintoc-Signature` header. The header contains a `t` timestamp and a `v1` signature. For example:

```text theme={null}
t=1620870928,v1=4df951e02db34a3f333bccad26d207993e9b14d78ac77cec026091991f567f6d
```

Each signature is a hash-based message authentication code (HMAC). Fintoc generates it from the raw request body and the timestamp, using SHA-256 and your [webhook endpoint](/reference/main-resources/webhook-endpoints/webhook-endpoints-object) secret.

## Validate the signature with the Fintoc SDK

If you use Node or Python, verify webhook signatures with the [Fintoc Node SDK](https://github.com/fintoc-com/fintoc-node) or [Fintoc Python SDK](https://github.com/fintoc-com/fintoc-python).

<CodeGroup>
  ```node Node theme={null}
  const { WebhookSignature, WebhookSignatureError } = require('fintoc');

  // Use the secret returned when you created the endpoint
  const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

  // Rest of your code here
  // ...

  // express.raw keeps req.body as the raw Buffer, which the signature covers.
  app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
    const payload = req.body;

    // Get the signature header
    const signature = req.headers['fintoc-signature'];

    try {
      // Verify the webhook signature
      WebhookSignature.verifyHeader(
        payload,
        signature,
        WEBHOOK_SECRET
      );

      // If verification passes, process the webhook
      const event = JSON.parse(payload.toString());

      // Rest of your code...

      // Acknowledge receipt of the event
      res.status(200).json({ received: true });
    } catch (error) {
      if (error instanceof WebhookSignatureError) {
        console.error('Webhook signature verification failed');
        res.status(400).json({
          error: 'Invalid signature',
          message: error.message
        });
      } else {
        res.status(500).json({ error: 'Webhook handler failed' });
      }
    }
  });
  ```

  ```python Python theme={null}
  import os

  from fintoc.webhook import WebhookSignature
  from fintoc.errors import WebhookSignatureError

  WEBHOOK_SECRET = os.getenv('FINTOC_WEBHOOK_SECRET')

  # Rest of your code
  # ...

  @app.route('/webhook', methods=['POST'])
  def handle_webhook():
    # Get the signature header
    signature = request.headers.get('Fintoc-Signature')

    # Get the raw request payload
    payload = request.get_data().decode('utf-8')

    # Verify the webhook signature
    try:
      WebhookSignature.verify_header(
        payload=payload,
        header=signature,
        secret=WEBHOOK_SECRET
      )

    except WebhookSignatureError as e:
      print('Invalid signature!')
      return str(e), 400

    # Acknowledge receipt of the event
    return '', 200
  ```
</CodeGroup>

For a complete working example, check the [full code here](https://github.com/fintoc-com/fintoc-python/blob/master/examples/webhook.py).

## Validate the signature with your own code

If you want to write your own implementation or use a different programming language, follow these steps:

### Extract the timestamp and the signature

Split the `Fintoc-Signature` header into an array by the `,` character. Then, split each element of the generated array by the `=` character to obtain a key-value pair. Finally, get the corresponding values for each key.

```python theme={null}
# using flask request
header_value = request.headers.get('Fintoc-Signature')
timestamp, event_signature = [x.split('=')[1] for x in header_value.split(',')]
```

### Rebuild the signed message

The signed message is the timestamp, a `.` character, and the raw body of the request. Rebuild the message with the timestamp you extracted from the `Fintoc-Signature` header.

```python theme={null}
import json

# using flask request
message = f"{timestamp}.{request.get_data().decode('utf-8')}"
```

Use the raw, unparsed request body directly from the request. Different libraries can represent the parsed JSON differently. For example, `message` should look like this:

```python theme={null}
'1626102791.{"id":"evt_DyzYBwdC07ao5MqG","type":"link.credentials_changed","mode":"test","created_at":"2021-07-12T15:11:09.875Z","data":{"id":"link_00000000","mode":"test","active":true,"object":"link","status":"active","accounts":null,"username":"111111111","holder_id":"111111111","created_at":"2021-06-24T00:00:00.000Z","link_token":null,"holder_type":"individual","institution":{"id":"cl_banco_bbva","name":"Banco BBVA","country":"cl"}},"object":"event"}'
```

### Generate the signature

Now that you have the message to sign, generate the signature using the SHA-256 hash function and the secret generated when creating the webhook endpoint.

```python theme={null}
import hmac
from hashlib import sha256

# The secret returned when you created the webhook endpoint
YOUR_WEBHOOK_SECRET = 'YOUR_WEBHOOK_SECRET'
encoded_secret = YOUR_WEBHOOK_SECRET.encode('utf-8')
encoded_message = message.encode('utf-8')
hmac_object = hmac.new(encoded_secret, msg=encoded_message, digestmod=sha256)
signature = hmac_object.hexdigest()
```

### Compare the signatures

Compare the signature you extracted from the `Fintoc-Signature` header with the one you just generated. If both signatures match, the event came from Fintoc.

```python theme={null}
import hmac

valid_signature = hmac.compare_digest(signature, event_signature)
```

## Prevent a replay attack

To prevent a replay attack, define a tolerance range for how old an event can be. When you receive an event, read the timestamp from the `Fintoc-Signature` header. Compare the timestamp with the current time. A tolerance of five minutes is a reasonable default. Accept events within five minutes of the current time, and discard older events.

## Test the integration

Confirm that signature validation works before you rely on it in production. Trigger a test event for your webhook endpoint from the Fintoc Dashboard, then check how your handler responds:

* With a valid signature, your handler validates the header and processes the event.
* With an invalid signature, for example after you change one character in the `v1` value or use the wrong secret, your handler rejects the event and returns a `400` status.
