> ## Documentation Index
> Fetch the complete documentation index at: https://api-doc.xmenu.it/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook security

> Validate webhook notifications to ensure they come from xMenu

Every webhook notification sent by xMenu includes a security signature that allows you to verify its authenticity.

## Security Header

Each webhook request contains the following header:

```http theme={null}
X-Xmenu-Signature: <hmac-sha256-signature>
```

The signature is an HMAC-SHA256 hash of the request payload, computed using your **Webhook Secret**.

## Webhook Secret

You can find your Webhook Secret in the <a href="https://app.xmenu.it/restaurant/" target="_blank">restaurant panel</a> under **Tools > API Access**.

## Signature Validation

To verify that a webhook notification is authentic:

1. Retrieve the `X-Xmenu-Signature` header from the incoming request
2. Compute the HMAC-SHA256 hash of the request body using your Webhook Secret
3. Compare the computed hash with the signature from the header
4. Reject the request if the signatures don't match

### Code Examples

<CodeGroup>
  ```php PHP theme={null}
  <?php
  $webhook_secret = 'your-webhook-secret';
  $req_headers = getallheaders();
  $signature = $req_headers['X-Xmenu-Signature'];
  $payload = file_get_contents('php://input');
  $calc_hmac = hash_hmac('sha256', $payload, $webhook_secret);

  if ($signature !== $calc_hmac) {
      header('HTTP/1.0 401 Unauthorized');
      exit('Webhook signature is not valid!');
  }

  $data = json_decode($payload);
  // Process the webhook notification
  ```

  ```javascript Node.js theme={null}
  const crypto = require('crypto');

  function verifyWebhook(req, res, next) {
      const webhookSecret = 'your-webhook-secret';
      const signature = req.headers['x-xmenu-signature'];
      const payload = JSON.stringify(req.body);
      const calcHmac = crypto
          .createHmac('sha256', webhookSecret)
          .update(payload)
          .digest('hex');

      if (signature !== calcHmac) {
          return res.status(401).send('Webhook signature is not valid!');
      }

      next();
  }
  ```

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

  WEBHOOK_SECRET = 'your-webhook-secret'

  def verify_webhook():
      signature = request.headers.get('X-Xmenu-Signature')
      payload = request.get_data()
      calc_hmac = hmac.new(
          WEBHOOK_SECRET.encode(),
          payload,
          hashlib.sha256
      ).hexdigest()

      if signature != calc_hmac:
          abort(401, 'Webhook signature is not valid!')
  ```

  ```csharp C# theme={null}
  using System.Security.Cryptography;
  using System.Text;

  public bool VerifyWebhook(string payload, string signature)
  {
      var webhookSecret = "your-webhook-secret";
      using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(webhookSecret));
      var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
      var calcHmac = Convert.ToHexString(hash).ToLower();

      return signature == calcHmac;
  }
  ```

  ```go Go theme={null}
  package main

  import (
      "crypto/hmac"
      "crypto/sha256"
      "encoding/hex"
      "net/http"
  )

  func verifyWebhook(r *http.Request, payload []byte) bool {
      webhookSecret := "your-webhook-secret"
      signature := r.Header.Get("X-Xmenu-Signature")

      mac := hmac.New(sha256.New, []byte(webhookSecret))
      mac.Write(payload)
      calcHmac := hex.EncodeToString(mac.Sum(nil))

      return signature == calcHmac
  }
  ```
</CodeGroup>

<Warning>
  Always validate the webhook signature before processing any data. Never trust incoming webhook requests without verification.
</Warning>
