Skip to main content
Browse docs
By Audience
Getting Started
Configuration
Use Cases
IDE Integration
Third-Party Integrations
Engineering Cache
Console
API Reference
Gateway
Workflow Guides
Templates
Providers and SDKs
Industry Guides
Advanced Guides
Browse by Role
Deployment Guides
In-Depth Guides
Tutorials
FAQ

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

EventFired when
event.blockedA request is blocked by a policy
event.allowedA request passes all policies
event.redactedContent is redacted from a response
escalation.createdAn escalation is raised
escalation.resolvedAn escalation is resolved
export.completedAn export job finishes
configuration.updatedA policy configuration is updated
configuration.deployedA configuration is deployed to a gateway

Webhook payload format

Every webhook POST includes these headers:

HeaderPurpose
Content-Typeapplication/json
X-Keeptrusts-EventEvent type (e.g., event.blocked)
X-Keeptrusts-DeliveryUnique delivery ID (UUID)
X-Keeptrusts-SignatureHMAC-SHA256 signature for verification
X-Keeptrusts-TimestampUnix 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:

AttemptDelay
1Immediate
230 seconds
32 minutes
410 minutes
51 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-Signature matches computed HMAC.
  • Idempotency: Use X-Keeptrusts-Delivery as 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