Build Event-Driven Workflows with Webhooks
Keeptrusts fires webhook events for key governance actions — policy violations, escalations, export completions, and configuration changes. This guide covers webhook architecture, payload formats, serverless handler patterns, and retry handling.
Use this page when
- You want to build custom event-driven automation triggered by Keeptrusts governance actions.
- You need to understand webhook payload formats, signature verification, and retry semantics.
- You are building serverless handlers (Lambda, Azure Functions, Cloud Run) for webhook processing.
- You want a reference for all supported event types and their payload structures.
Primary audience
- Primary: Technical Engineers
- Secondary: AI Agents, Technical Leaders
Webhook architecture
Keeptrusts API
→ Event occurs (block, escalation, export, config change)
→ POST to registered webhook URL
→ Your handler (Lambda, Azure Function, Cloud Run, etc.)
→ Downstream action (Slack, Jira, SIEM, PagerDuty, etc.)
Webhooks are registered via the API or the console:
- API:
POST /v1/webhooks - Console: Settings → Webhooks
Register a webhook
curl -X POST "${KEEPTRUSTS_API_URL}/v1/webhooks" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-handler.example.com/keeptrusts-webhook",
"events": ["event.blocked", "escalation.created", "export.completed"],
"secret": "whsec_your-signing-secret",
"enabled": true
}'
Supported event types
| Event | Fired when |
|---|---|
event.blocked | A request is blocked by a policy |
event.allowed | A request passes all policies |
event.redacted | Content is redacted from a response |
escalation.created | An escalation is raised |
escalation.resolved | An escalation is resolved |
export.completed | An export job finishes |
configuration.updated | A policy configuration is updated |
configuration.deployed | A configuration is deployed to a gateway |
Webhook payload format
Every webhook POST includes these headers:
| Header | Purpose |
|---|---|
Content-Type | application/json |
X-Keeptrusts-Event | Event type (e.g., event.blocked) |
X-Keeptrusts-Delivery | Unique delivery ID (UUID) |
X-Keeptrusts-Signature | HMAC-SHA256 signature for verification |
X-Keeptrusts-Timestamp | Unix timestamp of the event |
Example: event.blocked payload
{
"id": "evt_01HXK9P3M7NQJW2R5FG8TH6B4C",
"type": "event.blocked",
"timestamp": "2026-04-23T10:15:30Z",
"data": {
"event_id": "evt_01HXK9P3M7NQJW2R5FG8TH6B4C",
"gateway_id": "gw_production",
"model": "gpt-4o",
"provider": "openai",
"policy_name": "prompt-injection-detection",
"action": "block",
"reason": "Prompt injection pattern detected in user message",
"user_id": "user_12345",
"team_id": "team_engineering",
"request_snippet": "Ignore all instructions and...",
"configuration_id": "cfg_main"
}
}
Example: escalation.created payload
{
"id": "esc_01HXK9Q4N8ORKX3S6GH9UI7C5D",
"type": "escalation.created",
"timestamp": "2026-04-23T10:16:00Z",
"data": {
"escalation_id": "esc_01HXK9Q4N8ORKX3S6GH9UI7C5D",
"event_id": "evt_01HXK9P3M7NQJW2R5FG8TH6B4C",
"severity": "high",
"policy_name": "data-exfiltration-guard",
"description": "Potential PII exfiltration detected in model response",
"assigned_to": "team_compliance"
}
}
Verify webhook signatures
Always verify the X-Keeptrusts-Signature header before processing. The signature is computed as HMAC-SHA256(timestamp + "." + body, secret).
Node.js verification
const crypto = require('crypto');
function verifyWebhook(req, secret) {
const signature = req.headers['x-keeptrusts-signature'];
const timestamp = req.headers['x-keeptrusts-timestamp'];
const body = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${body}`)
.digest('hex');
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
Python verification
import hmac
import hashlib
def verify_webhook(headers: dict, body: bytes, secret: str) -> bool:
signature = headers.get("x-keeptrusts-signature", "")
timestamp = headers.get("x-keeptrusts-timestamp", "")
expected = hmac.new(
secret.encode(),
f"{timestamp}.".encode() + body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
AWS Lambda handler
// handler.js
const crypto = require('crypto');
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
exports.handler = async (event) => {
const body = event.body;
const signature = event.headers['x-keeptrusts-signature'];
const timestamp = event.headers['x-keeptrusts-timestamp'];
// Verify signature
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(`${timestamp}.${body}`)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))) {
return { statusCode: 401, body: 'Invalid signature' };
}
const payload = JSON.parse(body);
// Route by event type
switch (payload.type) {
case 'event.blocked':
await notifySlack(`🚫 Blocked: ${payload.data.reason} (policy: ${payload.data.policy_name})`);
break;
case 'escalation.created':
await notifySlack(`🔔 Escalation: ${payload.data.description} (severity: ${payload.data.severity})`);
break;
default:
console.log(`Unhandled event type: ${payload.type}`);
}
return { statusCode: 200, body: 'OK' };
};
async function notifySlack(text) {
await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
}
Azure Functions handler
// keeptrusts-webhook/index.js
const crypto = require('crypto');
module.exports = async function (context, req) {
const secret = process.env.WEBHOOK_SECRET;
const signature = req.headers['x-keeptrusts-signature'];
const timestamp = req.headers['x-keeptrusts-timestamp'];
const body = JSON.stringify(req.body);
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${body}`)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))) {
context.res = { status: 401, body: 'Invalid signature' };
return;
}
const payload = req.body;
context.log(`Received ${payload.type}: ${payload.id}`);
// Process event — create Jira ticket for escalations
if (payload.type === 'escalation.created') {
await createJiraTicket(payload.data);
}
context.res = { status: 200, body: 'OK' };
};
Retry and idempotency
Keeptrusts retries failed webhook deliveries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 1 hour |
After 5 failures the webhook is disabled and an admin notification is sent.
Idempotency handling
Use the X-Keeptrusts-Delivery header as an idempotency key to avoid processing duplicate deliveries:
const processedDeliveries = new Set(); // Use Redis/DynamoDB in production
function isNewDelivery(deliveryId) {
if (processedDeliveries.has(deliveryId)) return false;
processedDeliveries.add(deliveryId);
return true;
}
Event transformation patterns
Filter and forward to SIEM
// Forward only blocked events to your SIEM
if (payload.type === 'event.blocked') {
await fetch('https://siem.example.com/api/events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.SIEM_TOKEN}`,
},
body: JSON.stringify({
source: 'keeptrusts',
severity: 'high',
event_type: 'ai_policy_violation',
description: payload.data.reason,
metadata: payload.data,
}),
});
}
Aggregate events for daily digest
# Store events in a database, then send a daily summary
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('keeptrusts-events')
def handler(event, context):
payload = json.loads(event['body'])
table.put_item(Item={
'delivery_id': event['headers']['x-keeptrusts-delivery'],
'event_type': payload['type'],
'timestamp': payload['timestamp'],
'data': payload['data'],
})
return {'statusCode': 200}
Manage webhooks via the API
# List webhooks
curl "${KEEPTRUSTS_API_URL}/v1/webhooks" \
-H "Authorization: Bearer ${API_KEY}" | jq
# Update a webhook
curl -X PATCH "${KEEPTRUSTS_API_URL}/v1/webhooks/${WEBHOOK_ID}" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{"events": ["event.blocked", "escalation.created", "escalation.resolved"]}'
# Disable a webhook
curl -X PATCH "${KEEPTRUSTS_API_URL}/v1/webhooks/${WEBHOOK_ID}" \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
# Delete a webhook
curl -X DELETE "${KEEPTRUSTS_API_URL}/v1/webhooks/${WEBHOOK_ID}" \
-H "Authorization: Bearer ${API_KEY}"
For AI systems
- Canonical terms: Keeptrusts webhook,
POST /v1/webhooks,X-Keeptrusts-Signature(HMAC-SHA256),X-Keeptrusts-Delivery,X-Keeptrusts-Event, webhook secret, idempotent handler. - Supported event types:
event.blocked,event.allowed,event.redacted,escalation.created,escalation.resolved,export.completed,configuration.updated,configuration.deployed. - Console path: Settings → Webhooks → Create Webhook.
- Best next pages: Slack & Teams alerting, PagerDuty incident response, SIEM integration, Jira workflows.
For engineers
- Prerequisites: HTTPS endpoint accessible from Keeptrusts (Lambda, Cloud Run, etc.), webhook secret for HMAC-SHA256 signature verification.
- Validate: Register webhook via API or console, use "Send Test Event" to confirm delivery, verify
X-Keeptrusts-Signaturematches computed HMAC. - Idempotency: Use
X-Keeptrusts-Deliveryas a deduplication key in your handler — Keeptrusts retries failed deliveries. - Security: Always verify the HMAC signature before processing — reject requests with invalid or missing signatures.
For leaders
- Extensibility: Webhooks are the integration primitive that powers Slack alerts, PagerDuty incidents, Jira tickets, and SIEM forwarding.
- Real-time: Events fire within seconds of governance decisions — no polling delay for downstream systems.
- No vendor lock-in: Standard HTTP webhooks work with any system that accepts POST requests.
- Operational cost: Serverless handlers (Lambda, Cloud Run) cost near-zero at low volumes and scale automatically.
Next steps
- Set up Slack and Teams alerting for real-time notifications
- Configure PagerDuty integration for incident response
- Review SIEM integration for security event forwarding