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

# Webhook signatures

> How to protect Acrity outbound webhooks with HMAC, secrets, and tests.

Outbound webhooks send Acrity events to external systems configured by the workspace. To protect delivery, use HTTPS, explicit headers, and an HMAC signature so the destination can verify authenticity.

## Where to configure

In the Console, open **Webhooks** in the workspace. Webhooks require a Workspace admin (platform admins also have access).

<Frame>
  <img src="https://mintcdn.com/techdriven/eiJQrWxdoD9fo1DY/images/console/webhooks-form.png?fit=max&auto=format&n=eiJQrWxdoD9fo1DY&q=85&s=098353239c88b43d58e760efd5a07664" alt="New outbound webhook form where the signing secret and delivery headers are configured" width="2213" height="1262" data-path="images/console/webhooks-form.png" />
</Frame>

Main fields:

| Field                   | Purpose                                                        |
| ----------------------- | -------------------------------------------------------------- |
| Name                    | Identifies the destination in the Console and audit trail      |
| URL                     | HTTPS endpoint that receives the event                         |
| Method                  | HTTP method for delivery (`POST`)                              |
| Content type            | Format accepted by the destination                             |
| Events                  | Which events trigger delivery                                  |
| Headers                 | Fixed headers, including destination tokens when needed        |
| HMAC                    | Signature the destination uses to verify authenticity          |
| Body template           | Custom payload when the destination requires a specific format |
| Ignore SSL Verification | Skips TLS certificate validation for the destination endpoint  |

<Warning>
  Leave **Ignore SSL Verification** off. Enabling it disables TLS certificate validation for the destination, which exposes deliveries to man-in-the-middle interception. Use it only for a short-lived internal test against a host with a self-signed certificate, and turn it off before production.
</Warning>

## HMAC

When HMAC is enabled, Acrity signs the payload sent to the destination with a shared secret. The destination must recalculate the signature with the same secret and reject messages that do not match.

Best practices:

* Use a long, random, unique secret per webhook.
* Store the secret in the receiver system's vault.
* Validate the signature before processing the event.
* Reject requests without HTTPS in production.
* Rotate the secret when exposure is suspected.

<Tip>
  Use the Console test feature before enabling the webhook in production flows.
</Tip>

## Verify the signature

When HMAC is enabled, Acrity signs each outbound delivery and sends the signature in a header. Recompute the signature on your side and compare it to the header before you process the payload.

Every delivery includes these headers:

| Header                | Description                                      |
| --------------------- | ------------------------------------------------ |
| `X-ACR-Signature-256` | Signature in the format `sha256=<lowercase hex>` |
| `X-ACR-Event`         | Event type for the delivery                      |
| `X-ACR-Delivery`      | Unique delivery ID                               |

The signature value is `sha256=` followed by the lowercase hex digest of an HMAC-SHA256 computed over the raw request body using the webhook secret.

To verify a delivery:

1. Read the raw request body exactly as received. Do not re-serialize or reformat it.
2. Compute HMAC-SHA256 over the raw body using the webhook secret.
3. Format the result as `sha256=` followed by the lowercase hex digest.
4. Compare your value against `X-ACR-Signature-256` using a constant-time comparison.
5. Reject the request if the values do not match.

```javascript theme={null}
import crypto from "node:crypto";

function verifySignature(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || "");

  // Lengths must match for timingSafeEqual; the length check itself is not secret.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

Compute the HMAC over the raw bytes of the body. Frameworks that parse JSON before you can read the raw body change the bytes and break verification, so capture the raw body first.

```mermaid theme={null}
sequenceDiagram
    participant A as Acrity
    participant R as Your endpoint
    A->>A: Compute HMAC-SHA256 over raw body with the secret
    A->>R: POST payload with X-ACR-Signature-256
    R->>R: Recompute HMAC-SHA256 over raw body with the secret
    R->>R: Constant-time compare with the header value
    R-->>A: 2xx when the signatures match, reject otherwise
```

## Replay protection

Acrity does not add timestamp-based replay protection to outbound deliveries, and the signature alone does not stop a captured request from being replayed. De-duplicate on the `X-ACR-Delivery` ID: record the IDs you have already processed and ignore repeats.

## Inbound verification

For inbound VCS webhooks, Acrity verifies the provider's webhook signature before processing the event, so deliveries that fail signature validation are rejected. This happens automatically and needs no configuration.

## Secret headers

Headers marked as secret are treated as secrets in the Console. When editing, leave the value blank to keep the saved secret, or enter a new value to replace it.

## Tests and deliveries

Use the webhook test to validate:

1. Connectivity to the URL.
2. Payload format.
3. Required headers.
4. HMAC validation in the receiver.
5. Destination response codes.

The detail page shows recent deliveries and helps diagnose failures such as an incorrect URL, timeout, invalid credential, certificate error, or rejected payload.

## Rotation

To rotate an HMAC secret:

<Steps>
  <Step title="Create a new secret in the receiver">
    Configure the new secret in the system that receives the webhook.
  </Step>

  <Step title="Update the webhook in Acrity">
    Edit the webhook and replace the HMAC secret.
  </Step>

  <Step title="Send test">
    Confirm that the receiver validates the new signature.
  </Step>

  <Step title="Enable normal flow">
    Track the first real deliveries after rotation.
  </Step>
</Steps>
