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
-
Go to Account Settings
-
Click Webhooks in the settings navigation
Step 2: Create a Webhook
-
Click New webhook
-
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
-
-
Click Create webhook
A signing secret is automatically generated. You'll need this to verify incoming requests.
Step 3: Copy the Signing Secret
-
Open the webhook detail page
-
Find the Signing secret section
-
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 withsha256= -
X-Humadroid-Timestamp— Unix timestamp of when the request was signed
Verification Steps
-
Read the raw request body (before any JSON parsing)
-
Compute
HMAC-SHA256(signing_secret, raw_body) -
Compare with the signature in the header (use constant-time comparison)
-
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:
-
Open the webhook detail page
-
See Recent deliveries at the bottom
-
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
-
Go to Account Settings > Webhooks
-
Click the pencil icon on the webhook row, or open the detail page and use the menu
-
Update the URL, project, events, or active status
-
Click Update webhook
Regenerating the Signing Secret
If your signing secret is compromised:
-
Open the webhook detail page
-
In the Signing secret section, click Regenerate
-
Confirm the action
-
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
-
From the index: click the trash icon
-
Or from the detail page: use the menu and select Delete
-
Confirm the deletion
Deleting a webhook also removes all its delivery history.
Best Practices
-
Always verify signatures - Never trust a webhook payload without checking the
X-Humadroid-Signatureheader -
Respond quickly - Return a 2xx response within 10 seconds; do heavy processing asynchronously
-
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_idfor published events,assignment_idfor acknowledged events) -
Monitor your endpoint - Check the delivery log periodically to catch failures early
-
Use HTTPS - Webhook URLs must use HTTPS to protect payload data in transit