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

# Verifying Signatures

> Confirm a webhook genuinely came from Payracle

Every webhook request includes an `X-Payracle-Signature` header — an HMAC-SHA256 signature of the raw JSON request body, signed with your secret key. Always verify this header before processing the event.

```http theme={null}
X-Payracle-Signature: sha256=3b5d9e2f1a...
```

<Warning>
  Compute the signature over the **raw request body bytes**, before any JSON parsing. Re-serializing a parsed object rarely produces byte-identical output (key order, whitespace), which will make a genuine webhook fail verification.
</Warning>

<CodeGroup>
  ```javascript Node.js / Express theme={null}
  const crypto = require('crypto');

  app.post('/webhook/payracle', express.raw({ type: 'application/json' }), (req, res) => {
    const signature = req.headers['x-payracle-signature'];
    const expected  = 'sha256=' + crypto
      .createHmac('sha256', process.env.PAYRACLE_SECRET_KEY)
      .update(req.body)          // raw Buffer — do NOT use req.body after JSON.parse
      .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
      return res.status(401).send('Invalid signature');
    }

    const event = JSON.parse(req.body);
    // handle event...
    res.sendStatus(200);
  });
  ```

  ```python Python / Flask theme={null}
  import hmac, hashlib, os
  from flask import Flask, request, abort

  app = Flask(__name__)

  @app.route('/webhook/payracle', methods=['POST'])
  def webhook():
      signature = request.headers.get('X-Payracle-Signature', '')
      expected  = 'sha256=' + hmac.new(
          os.environ['PAYRACLE_SECRET_KEY'].encode(),
          request.get_data(),
          hashlib.sha256
      ).hexdigest()

      if not hmac.compare_digest(signature, expected):
          abort(401)

      event = request.get_json()
      # handle event...
      return '', 200
  ```

  ```php PHP / Laravel theme={null}
  <?php
  // PHP / Laravel
  $signature = $request->header('X-Payracle-Signature');
  $expected  = 'sha256=' . hash_hmac('sha256', $request->getContent(), config('services.payracle.secret_key'));

  if (!hash_equals($expected, $signature)) {
      abort(401, 'Invalid signature');
  }

  $event = $request->json()->all();
  // handle event...
  ```
</CodeGroup>
