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

# Verifying SendX Webhook

> Making sure your webhooks come from the correct source

When SendX sends webhooks to your endpoint, we include a secure **HMAC-SHA256 signature**. This allows you to verify that the webhook truly came from SendX and that the payload hasn’t been altered in transit.

This article explains how SendX signs webhook requests and how you can verify them in your own server — with ready-to-use examples in **Python** and **Node.js**.

## Why Signature Verification Matters

Webhook signature verification ensures:

* ✅ The request is **authentic** (it came from SendX).
* ✅ The payload **has not been altered** during transmission.
* ✅ You can safely process the event data without risk of spoofing.

## How SendX Signs Webhooks

Every webhook request sent from SendX includes a **cryptographic signature** in its HTTP headers.

Here’s how the signature is generated:

1. The webhook payload (JSON body) is taken as-is.
2. SendX computes an **HMAC-SHA256** hash of this body using your team’s unique **API key** as the secret.
3. The result is hex-encoded and added to the request headers.

Example request headers:

```
Content-Type: application/json
X-SendX-Signature: 3e7bda1d2d3c74b5f8a5ce5e7e7efef2df...
X-SendX-Signature-Alg: hmac-sha256
X-SendX-Webhook-Id: 4b1b3f72-8e01-43aa-9cf9-5dc6a8e4e937
X-SendX-Webhook-Attempt: 1
```

## How to Verify a Webhook on Your Server

When your server receives a webhook from SendX:

1. **Capture the raw body** of the POST request (without parsing it).
2. Retrieve the following headers:
   * `X-SendX-Signature`
   * `X-SendX-Signature-Alg`
3. Recompute the HMAC using your **SendX Team API Key**.
4. Compare the recomputed signature with the one in the header using a constant-time comparison.

If they match — the webhook is valid.

### Example: Verification Script

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib
  from flask import Flask, request, jsonify

  app = Flask(__name__)

  # Replace with your actual Team API Key
  TEAM_API_KEY = "YOUR_TEAM_API_KEY_HERE"

  @app.route("/webhook", methods=["POST"])
  def verify_webhook():
      raw_body = request.data
      received_sig = request.headers.get("x-sendx-signature", "")
      algo = request.headers.get("x-sendx-signature-alg", "")

      if algo.lower() != "hmac-sha256":
          return jsonify({"error": "unsupported signature algorithm"}), 400

      computed_sig = hmac.new(
          TEAM_API_KEY.encode("utf-8"),
          raw_body,
          hashlib.sha256
      ).hexdigest()

      if hmac.compare_digest(computed_sig, received_sig):
          print("✅ Webhook signature verified successfully!")
          return jsonify({"verified": True}), 200
      else:
          print("❌ Invalid webhook signature!")
          return jsonify({"verified": False}), 400

  if __name__ == "__main__":
      app.run(host="0.0.0.0", port=5000)
  ```

  ```javascript JavaScript theme={null}
  import express from 'express';
  import crypto from 'crypto';

  const app = express();

  // Use raw body middleware to get exact payload (important!)
  app.use(express.raw({ type: 'application/json' }));

  const SENDX_API_KEY = 'your_team_api_key_here';

  function verifySignature(payload, headerSignature) {
    const computedSignature = crypto
      .createHmac('sha256', SENDX_API_KEY)
      .update(payload)
      .digest('hex');

    return crypto.timingSafeEqual(
      Buffer.from(computedSignature),
      Buffer.from(headerSignature)
    );
  }

  app.post('/webhook', (req, res) => {
    const signature = req.headers['x-sendx-signature'];
    const payload = req.body;

    if (!signature) {
      return res.status(400).json({ error: 'Missing signature header' });
    }

    if (!verifySignature(payload, signature)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    const data = JSON.parse(payload);
    console.log('✅ Verified webhook:', data);

    res.status(200).json({ status: 'success' });
  });

  app.listen(5000, () => console.log('Webhook listener running on port 5000'));
  ```
</CodeGroup>

### Common Issues & Fixes

| **Issue**                         | **Possible Cause**                          | **Solution**                                                                     |
| :-------------------------------- | :------------------------------------------ | :------------------------------------------------------------------------------- |
| Invalid Signature                 | Payload was modified or encoded incorrectly | Make sure to use the **raw request body**, not a parsed or re-serialized version |
| Missing header                    | Endpoint not receiving all headers          | Ensure your reverse proxy or framework doesn’t strip custom headers              |
| Verification fails intermittently | Using the wrong API Key                     | Confirm you’re using your **Team API Key**, not a personal or project key        |
