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

# Alert Authentication

> Verify that incoming webhook requests are genuinely from Leaf by validating the HMAC SHA-256 signature in the X-Leaf-Signature header.

Every webhook request from Leaf includes an `X-Leaf-Signature` header. This header contains a base64-encoded HMAC SHA-256 digest of the request body, signed with the secret you provided when creating the webhook. Verify this signature before processing any payload.

## How signature verification works

1. Read the raw request body as bytes. Do not parse or reformat it first.
2. Compute an HMAC SHA-256 digest of those bytes using your webhook secret as the key.
3. Base64-decode the value from the `X-Leaf-Signature` header.
4. Compare the two digests using a constant-time comparison function.

The signed content is a compact JSON string without extra line breaks or spaces (other than spaces after `:` and `,`).

## Code examples

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

  def verify_leaf_signature(raw_body: bytes, secret: str, signature_header: str) -> bool:
      expected_sig = hmac.digest(
          msg=raw_body,
          key=secret.encode("utf-8"),
          digest="sha256"
      )
      request_sig = base64.b64decode(signature_header)
      return hmac.compare_digest(expected_sig, request_sig)
  ```

  ```java Java theme={null}
  import java.util.Base64;
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import java.security.MessageDigest;

  public boolean verifyLeafSignature(byte[] rawBody, String secret, byte[] signatureHeader) throws Exception {
      Mac mac = Mac.getInstance("HmacSHA256");
      mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
      byte[] expected = mac.doFinal(rawBody);
      return MessageDigest.isEqual(expected, signatureHeader);
  }
  ```

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

  function verifyLeafSignature(rawBody, secret, signatureHeader) {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(rawBody)
      .digest();
    const received = Buffer.from(signatureHeader, "base64");
    return crypto.timingSafeEqual(expected, received);
  }
  ```
</CodeGroup>

## Example payload

If you receive a `fieldCreated` event, the signed body looks like:

```json theme={null}
{"source": "REST", "leafUserId": "uuid", "fieldId": "uuid", "timestamp": "2024-06-15T14:30:00.000000Z", "type": "fieldCreated"}
```

Compute the HMAC of this exact byte string with your secret to get the expected signature.

## IP addresses

Leaf uses cloud infrastructure and does not have a fixed range of IP addresses for webhook delivery. If your network architecture requires allow-listing, consider placing a load balancer or reverse proxy in a DMZ to receive webhook traffic and forward it to your internal systems.

<Tip>
  Using an `X-CompanyName-Signature` header for webhook verification is an industry-standard pattern also used by Twilio, Slack, and Stripe.
</Tip>

## What to do next

* [Alerts Overview](/alerts/overview) for webhook setup and retry policy.
* [Events reference](/alerts/events) for all event types and payload schemas.
* [Alerts API Reference](/api-reference/alerts) for endpoint details.
