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

# Verificação de Assinatura

> Como verificar a autenticidade dos webhooks recebidos

Se você configurou um **secret** no webhook, cada requisição inclui o header `X-Webhook-Signature` com uma assinatura HMAC-SHA256 do body.

## Como Verificar

A assinatura é calculada assim:

```
HMAC-SHA256(secret, request_body)
```

### Exemplos de Verificação

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode('utf-8'),
          body,
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(expected, signature)

  # No seu endpoint Flask/FastAPI:
  @app.post("/webhook")
  async def handle_webhook(request: Request):
      body = await request.body()
      signature = request.headers.get("X-Webhook-Signature", "")

      if not verify_webhook(body, signature, WEBHOOK_SECRET):
          return Response(status_code=401)

      payload = json.loads(body)
      # Processar o evento...
      return Response(status_code=200)
  ```

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

  function verifyWebhook(body, signature, secret) {
    const expected = crypto
      .createHmac('sha256', secret)
      .update(body)
      .digest('hex');
    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(signature)
    );
  }

  // No seu endpoint Express:
  app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
    const signature = req.headers['x-webhook-signature'];
    const body = req.body;

    if (!verifyWebhook(body, signature, process.env.WEBHOOK_SECRET)) {
      return res.status(401).send('Assinatura inválida');
    }

    const payload = JSON.parse(body);
    // Processar o evento...
    res.status(200).send('OK');
  });
  ```

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

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

  func verifyWebhook(body []byte, signature, secret string) bool {
      mac := hmac.New(sha256.New, []byte(secret))
      mac.Write(body)
      expected := hex.EncodeToString(mac.Sum(nil))
      return hmac.Equal([]byte(expected), []byte(signature))
  }

  func webhookHandler(w http.ResponseWriter, r *http.Request) {
      body, _ := io.ReadAll(r.Body)
      signature := r.Header.Get("X-Webhook-Signature")

      if !verifyWebhook(body, signature, os.Getenv("WEBHOOK_SECRET")) {
          w.WriteHeader(http.StatusUnauthorized)
          return
      }

      // Processar o evento...
      w.WriteHeader(http.StatusOK)
  }
  ```
</CodeGroup>

<Warning>
  Sempre use comparação em tempo constante (`hmac.compare_digest` no Python, `timingSafeEqual` no Node.js) para evitar ataques de timing.
</Warning>

## Boas Práticas

1. **Sempre verifique a assinatura** antes de processar o webhook
2. Use **comparação em tempo constante** para evitar timing attacks
3. **Responda 200 rapidamente** e processe o evento de forma assíncrona
4. Implemente **idempotência** usando `dispatchId` + `status` como chave de deduplicação
