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

# Verify Webhook Signatures From Hive Inspect

> Check the signature on every Hive Inspect webhook so you know the message came from Hive and was not changed. Includes Python and Node.js examples.

<Note>
  **The Hive Inspect API is a work in progress.** It is available to a small group of accounts today, and the release for everyone is coming very soon. To ask for early access, message us from the chat bubble in your dashboard.
</Note>

Anyone who learns your endpoint URL could send it a fake message. Hive signs every webhook with your endpoint's signing secret, so you can check that a message is real before you act on it.

## How the signature works

Each message has two headers:

```
X-HiveInspect-Timestamp: 1790953451
X-HiveInspect-Signature: t=1790953451,v1=5f1c0a…e9
```

The `v1` value is an HMAC-SHA256 of the timestamp, a dot, and the raw request body, using your signing secret as the key.

## Verify a message

<Steps titleSize="h3">
  <Step title="Read the raw body">
    Use the request body exactly as it arrived, before any JSON parsing. Parsing and re-writing the JSON changes the bytes and breaks the check.
  </Step>

  <Step title="Take the timestamp and signature from the header">
    Split `X-HiveInspect-Signature` on the comma. `t=` is the timestamp and `v1=` is the signature.
  </Step>

  <Step title="Compute the expected signature">
    Build the string `timestamp.body` and compute its HMAC-SHA256 with your signing secret. Write the result as lowercase hex.
  </Step>

  <Step title="Compare the two">
    Use a constant-time comparison. If they differ, reply `400` and ignore the message.
  </Step>

  <Step title="Check the age">
    Reject a message whose timestamp is more than 5 minutes old. This stops someone from re-sending a message they captured earlier.
  </Step>
</Steps>

## Examples

<CodeGroup>
  ```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
  import hashlib
  import hmac
  import time

  def is_from_hive(secret: str, raw_body: bytes, signature_header: str) -> bool:
      parts = dict(p.split("=", 1) for p in signature_header.split(",") if "=" in p)
      timestamp, received = parts.get("t"), parts.get("v1")
      if not timestamp or not received:
          return False
      if abs(time.time() - int(timestamp)) > 300:
          return False
      message = timestamp.encode() + b"." + raw_body
      expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
      return hmac.compare_digest(expected, received)
  ```

  ```javascript Node.js theme={"theme":{"light":"github-light","dark":"vesper"}}
  const crypto = require("crypto");

  function isFromHive(secret, rawBody, signatureHeader) {
    const parts = Object.fromEntries(
      signatureHeader.split(",").map((p) => p.split(/=(.*)/s).slice(0, 2))
    );
    const timestamp = parts.t;
    const received = parts.v1;
    if (!timestamp || !received) return false;
    if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
    const expected = crypto
      .createHmac("sha256", secret)
      .update(timestamp + "." + rawBody)
      .digest("hex");
    const a = Buffer.from(expected);
    const b = Buffer.from(received);
    return a.length === b.length && crypto.timingSafeEqual(a, b);
  }
  ```
</CodeGroup>

In Node.js, `rawBody` must be the unparsed request body as a string. With Express, use `express.raw({ type: "application/json" })` on the webhook route and pass `req.body.toString()`.

## Change the signing secret

Open the endpoint's menu on the **API Access** page and choose **New signing secret**. Hive shows the new secret once.

<Warning>
  The old secret stops working right away. Messages sent after the change are signed with the new secret, including retries of earlier events. Update your receiver as soon as you create the new secret.
</Warning>

## Things to know

* **Each endpoint has its own secret.** Changing one does not affect the others.
* **Hive shows a secret once.** If you lose it, create a new one.
* **A retry has a new timestamp and a new signature.** The body and the event `id` stay the same.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.