## **Overview**

Webhooks let you notify external systems in real-time when compliance events occur in Humadroid. When a policy is published or acknowledged, Humadroid sends an HTTP POST request to a URL you configure with a JSON payload describing the event.

### Use Cases

* **Trust Center sync** - Automatically update a self-hosted trust center when policies are published

* **Email distribution** - Trigger notifications to specific distribution lists on policy changes

* **Audit trail** - Feed compliance events into an external SIEM or logging system

* **Workflow automation** - Kick off downstream processes in tools like Zapier, Make, or custom systems

### Supported Events

* **`compliance.document_published`** — fires when a policy or document is finalized (published)

* **`compliance.document_acknowledged`** — fires when a user accepts, rejects, or reads an assigned policy

---

## **Setting Up a Webhook**

### Prerequisites

* Admin access to Humadroid

* At least one compliance project

* An HTTPS endpoint that can receive POST requests

### Step 1: Navigate to Webhooks

1. Go to **Account Settings**

2. Click **Webhooks** in the settings navigation

### Step 2: Create a Webhook

1. Click **New webhook**

2. Fill in the form:

   * **URL** - The HTTPS endpoint that will receive events (e.g., `https://yourapp.com/webhooks/humadroid`)

   * **Compliance project** - Only events for documents in this project will trigger the webhook

   * **Events** - Select which events to subscribe to

   * **Active** - Toggle to enable/disable the webhook

3. Click **Create webhook**

A signing secret is automatically generated. You'll need this to verify incoming requests.

### Step 3: Copy the Signing Secret

1. Open the webhook detail page

2. Find the **Signing secret** section

3. Copy the secret and store it securely in your application's configuration

---

## **Payload Format**

Every webhook request is an HTTP POST with a JSON body. The payload has a standard envelope:

```
{
  "event": "compliance.document_published",
  "timestamp": "2026-04-03T12:00:00Z",
  "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": { ... }
}
```

### Policy Published (`compliance.document_published`)

Fired when a document transitions to the "Published" state.

```
{
  "event": "compliance.document_published",
  "timestamp": "2026-04-03T12:00:00Z",
  "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "document_id": "a1b2c3d4-...",
    "title": "Information Security Policy",
    "version": "2.0",
    "document_type": "policy",
    "published_at": "2026-04-03T12:00:00Z",
    "project_ids": ["p1q2r3s4-..."],
    "author": {
      "id": "u1v2w3x4-...",
      "name": "Jane Smith",
      "email": "jane@example.com"
    }
  }
}
```

### Policy Acknowledged (`compliance.document_acknowledged`)

Fired when a user responds to an assigned policy.

```
{
  "event": "compliance.document_acknowledged",
  "timestamp": "2026-04-03T14:30:00Z",
  "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "document_id": "a1b2c3d4-...",
    "title": "Information Security Policy",
    "assignment_id": "x9y8z7w6-...",
    "user_id": "u1v2w3x4-...",
    "status": "accepted",
    "responded_at": "2026-04-03T14:30:00Z"
  }
}
```

The `status` field is one of: `accepted`, `rejected`, or `read`.

---

## **Verifying Webhook Signatures**

Every request includes two headers for verification:

* **`X-Humadroid-Signature`** — HMAC-SHA256 signature of the raw request body, prefixed with `sha256=`

* **`X-Humadroid-Timestamp`** — Unix timestamp of when the request was signed

### Verification Steps

1. Read the raw request body (before any JSON parsing)

2. Compute `HMAC-SHA256(signing_secret, raw_body)`

3. Compare with the signature in the header (use constant-time comparison)

4. Optionally check the timestamp to prevent replay attacks (reject if older than 5 minutes)

### Example: Ruby

```
def verify_webhook(request, signing_secret)
  body = request.body.read
  expected = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", signing_secret, body)
  signature = request.headers["X-Humadroid-Signature"]
​
  ActiveSupport::SecurityUtils.secure_compare(expected, signature)
end
```

### Example: Node.js

```
const crypto = require('crypto');
​
function verifyWebhook(body, signature, signingSecret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', signingSecret)
    .update(body, 'utf8')
    .digest('hex');
​
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
```

### Example: Python

```
import hmac
import hashlib
​
def verify_webhook(body: bytes, signature: str, signing_secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        signing_secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
```

---

## **Retry Behavior**

If your endpoint returns a non-2xx status code or is unreachable, Humadroid retries with exponential backoff:

* **Attempt 1** — immediate

* **Attempt 2** — ~2 minutes

* **Attempt 3** — ~4 minutes

* **Attempt 4** — ~8 minutes

* **Attempt 5** — ~16 minutes

* **Attempt 6** — ~32 minutes

* **Attempt 7** — ~1 hour

* **Attempt 8** — ~2 hours

* **Attempt 9** — ~4 hours

* **Attempt 10** — ~8 hours

After **10 consecutive failures**, the webhook is automatically disabled. You can re-enable it from the webhook detail page once the issue is resolved.

---

## **Monitoring Deliveries**

### Delivery Log

Each webhook has a delivery log showing all events sent:

1. Open the webhook detail page

2. See **Recent deliveries** at the bottom

3. Click **View all deliveries** for the full paginated log

### Delivery Details

Click on any delivery to see:

* **Overall status** - Delivered, Failed, or Pending

* **Attempt history** - Each retry attempt with full request/response details:

  * Request URL, headers, and body

  * Response status code, headers, and body

  * Timestamp of each attempt

This is useful for debugging integration issues — you can see exactly what was sent and what your endpoint returned.

---

## **Managing Webhooks**

### Editing a Webhook

1. Go to **Account Settings > Webhooks**

2. Click the pencil icon on the webhook row, or open the detail page and use the menu

3. Update the URL, project, events, or active status

4. Click **Update webhook**

### Regenerating the Signing Secret

If your signing secret is compromised:

1. Open the webhook detail page

2. In the **Signing secret** section, click **Regenerate**

3. Confirm the action

4. Update your application with the new secret

Note: The old secret is immediately invalidated. Update your receiving application before regenerating, or expect failed deliveries until the new secret is configured.

### Disabling a Webhook

You can temporarily disable a webhook without deleting it:

* From the index: click the pause icon

* From the detail page: use the menu and select **Disable**

A disabled webhook does not receive any events. Re-enable it the same way.

### Deleting a Webhook

1. From the index: click the trash icon

2. Or from the detail page: use the menu and select **Delete**

3. Confirm the deletion

Deleting a webhook also removes all its delivery history.

---

## **Best Practices**

1. **Always verify signatures** - Never trust a webhook payload without checking the `X-Humadroid-Signature` header

2. **Respond quickly** - Return a 2xx response within 10 seconds; do heavy processing asynchronously

3. **Handle duplicates** - In rare cases (e.g., network issues), you may receive the same event twice; deduplicate using `webhook_id` + `timestamp` + the event-specific entity ID (`document_id` for published events, `assignment_id` for acknowledged events)

4. **Monitor your endpoint** - Check the delivery log periodically to catch failures early

5. **Use HTTPS** - Webhook URLs must use HTTPS to protect payload data in transit