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

# Signature Validation

> Every webhook request is signed with HMAC-SHA256 using your endpoint secret. Validate before trusting the payload.

Every webhook request is signed with **HMAC-SHA256** using your endpoint secret. Validate the signature before trusting the payload.

<Warning>
  Webhook endpoints are publicly reachable URLs. Anyone who knows the URL can send a `POST` to it. **Always validate the signature** before processing. Don't take an unsigned or invalid-signature request seriously.
</Warning>

## Headers

```http theme={null}
POST /your-webhook-endpoint HTTP/1.1
Host: your-domain.com
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
X-Event-Type: message.delivered
X-Event-ID: msg_7f8a9b0c1d2e3f4g
X-Timestamp: 1705488600
```

## Node.js example

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

function validateWebhook(req, webhookSecret) {
  const signature = req.headers['x-webhook-signature'];
  const body = JSON.stringify(req.body);

  const hmac = crypto.createHmac('sha256', webhookSecret);
  hmac.update(body);
  const expectedSignature = `sha256=${hmac.digest('hex')}`;

  return signature === expectedSignature;
}

app.post('/webhooks', (req, res) => {
  if (!validateWebhook(req, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  // Process webhook...
  res.status(200).send('OK');
});
```

## Tips

* **Use a constant-time comparison** for the signature check (e.g. `crypto.timingSafeEqual` in Node) to avoid timing attacks.
* **Stringify the body identically to how it was received** - middleware that reformats JSON will break the signature. Either store the raw body before parsing or use a body parser that preserves the exact byte sequence.
* **Reject stale events** by checking `X-Timestamp` is within (e.g.) 5 minutes of now. Combined with signature validation, this protects against replay attacks.
* **Don't log the secret.** It should only ever appear in your secret manager and in memory inside the validation routine.
