Receive Aegis Firma events in real time. Works with any language or framework. HMAC-SHA256 verified — secure from the start.
Quick start
Go to Dashboard → Integrations, click Add Webhook, enter your endpoint URL, and copy the signing secret. Then implement the verification code below.
Aegis Firma fires these events to your endpoint:
| Event | Description |
|---|---|
task.completed | A compliance task was marked complete |
score.changed | Compliance score changed by ≥2 points |
regulation.new | New regulatory development detected |
breach.detected | Monitoring gap or breach detected |
vendor.risk_changed | Vendor risk score updated significantly |
test.ping | Manual test from Integrations page |
Every webhook is a JSON POST with this structure:
{
"event": "score.changed",
"org_id": "org_abc123",
"timestamp": "2026-04-19T10:30:00.000Z",
"data": {
"previous": 72,
"current": 81,
"delta": 9,
"direction": "up"
}
}Every request includes an X-Aegis Firma-Signature header — an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret. Always verify this before processing the event.
// Node.js + Express
const crypto = require('crypto')
const express = require('express')
const app = express()
app.use(express.json({ verify: (req, res, buf) => {
req.rawBody = buf
}}))
app.post('/webhook', (req, res) => {
const secret = process.env.COMPLIANCEIQ_WEBHOOK_SECRET
const signature = req.headers['x-aegisfirma-signature']
const expected = crypto
.createHmac('sha256', secret)
.update(req.rawBody)
.digest('hex')
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)) {
return res.status(401).send('Invalid signature')
}
const { event, data } = req.body
console.log('Received event:', event, data)
// Handle events
switch (event) {
case 'score.changed':
if (data.direction === 'down') {
// Alert your team
sendSlackAlert(`Compliance score dropped: ${data.previous} → ${data.current}`)
}
break
case 'regulation.new':
// Log to your system
saveRegulation(data)
break
}
res.status(200).json({ received: true })
})# Python + Flask
import hmac
import hashlib
from flask import Flask, request, abort
import os
app = Flask(__name__)
def verify_signature(payload: bytes, signature: str) -> bool:
secret = os.environ['COMPLIANCEIQ_WEBHOOK_SECRET'].encode()
expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Aegis Firma-Signature', '')
if not verify_signature(request.data, signature):
abort(401)
payload = request.get_json()
event = payload['event']
data = payload['data']
print(f'Received event: {event}', data)
if event == 'score.changed' and data['direction'] == 'down':
send_alert(f"Compliance score dropped: {data['previous']} → {data['current']}")
elif event == 'regulation.new':
save_regulation(data)
return {'received': True}, 200<?php
// PHP (no framework)
$secret = $_ENV['COMPLIANCEIQ_WEBHOOK_SECRET'];
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_COMPLIANCEIQ_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
$body = json_decode($payload, true);
$event = $body['event'];
$data = $body['data'];
error_log("Received event: $event");
switch ($event) {
case 'score.changed':
if ($data['direction'] === 'down') {
// Alert team
sendSlackAlert("Compliance score dropped: {$data['previous']} → {$data['current']}");
}
break;
case 'regulation.new':
saveRegulation($data);
break;
}
header('Content-Type: application/json');
echo json_encode(['received' => true]);// Go + net/http
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"log"
"net/http"
"os"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
secret := []byte(os.Getenv("COMPLIANCEIQ_WEBHOOK_SECRET"))
mac := hmac.New(sha256.New, secret)
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
signature := r.Header.Get("X-Aegis Firma-Signature")
if !hmac.Equal([]byte(expected), []byte(signature)) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
var payload struct {
Event string `json:"event"`
Data json.RawMessage `json:"data"`
}
json.Unmarshal(body, &payload)
log.Printf("Received event: %s", payload.Event)
switch payload.Event {
case "score.changed":
// Handle score change
var data struct {
Previous int `json:"previous"`
Current int `json:"current"`
Direction string `json:"direction"`
}
json.Unmarshal(payload.Data, &data)
if data.Direction == "down" {
sendAlert(data.Previous, data.Current)
}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{"received": true})
}10 seconds per request. Return 2xx quickly — do heavy processing async.
3 attempts on non-2xx or timeout: immediately, +30s, +5min.
Use the event timestamp + org_id to deduplicate retried events.
From the Integrations page, click the Test button on any webhook — this fires a test.ping event to your endpoint.
Use webhook.site to inspect raw payloads during development.