# Verify signatures

Every request includes an `X-Hooks-Signature` header. Verify it against the raw body using your endpoint's signing secret before acting on the event.

The header contains a timestamp and one or more signatures: `t=1739980800,v1=8f2c…`. Compute `HMAC-SHA256(secret, timestamp + "." + rawBody)` and compare it in constant time.

```js
const crypto = require("node:crypto");

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");

  return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
```

## Provider-specific schemes

When the endpoint uses a known provider, Hooks verifies the provider's own signature instead of the generic scheme:

| Provider | Header                | Scheme                                                                               |
| -------- | --------------------- | ------------------------------------------------------------------------------------ |
| `github` | `X-Hub-Signature-256` | `sha256=<hex HMAC of raw body>`.                                                     |
| `stripe` | `Stripe-Signature`    | `t=<ts>,v1=<hex HMAC of "<ts>.<body>">`, 5-minute tolerance, multiple `v1` accepted. |
| `slack`  | `X-Slack-Signature`   | `v0=<hex HMAC of "v0:<ts>:<body>">`, 5-minute tolerance.                             |
| `linear` | `Linear-Signature`    | Hex HMAC of the raw body plus a body `webhookTimestamp` within 60 seconds.           |

Provider signatures are checked against the endpoint secret you configured in the console.

> Never compare signatures with `==`. Use a constant-time function and reject requests whose timestamp is more than five minutes old to limit replay attacks.
