Behavry Webhooks
Behavry can deliver real-time alert notifications to external HTTP endpoints — Slack, PagerDuty, custom SIEM ingestion pipelines, or any webhook receiver.
Webhooks fire on alert events that meet a configurable minimum severity threshold. They are fire-and-forget with one retry on failure, so they never block the internal event pipeline.
Configuration
Set environment variables before starting the backend:
# Comma-separated list of target URLs
BEHAVRY_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx
# HMAC-SHA256 signing key (choose any strong random string)
BEHAVRY_WEBHOOK_SECRET=your-secret-signing-key
# Minimum severity to deliver: low | medium | high | critical (default: high)
BEHAVRY_WEBHOOK_MIN_SEVERITY=high
Multiple endpoints are supported:
BEHAVRY_WEBHOOK_URL=https://hooks.slack.com/services/...,https://ingest.pagerduty.com/...
All endpoints share the same secret and minimum severity threshold.
Payload Format
Each webhook delivery is a signed HTTP POST with Content-Type: application/json.
{
"event_type": "alert",
"timestamp": "2026-02-20T10:30:00.123Z",
"agent_id": "agt_abc123",
"session_id": "sess_xyz789",
"severity": "critical",
"alert_type": "dlp_violation",
"tool_name": "read_file",
"mcp_server": "demo-filesystem",
"action": "tools/call",
"resource": "/etc/passwd",
"policy_result": "deny",
"policy_reason": "DLP: system_file pattern matched",
"dlp_findings": [
{
"pattern": "system_file",
"severity": "critical",
"field": "path"
}
],
"behavioral_score": 0.87,
"latency_ms": 42
}
Field Reference
| Field | Type | Description |
|---|---|---|
event_type | string | Always "alert" for webhook deliveries |
timestamp | ISO 8601 | UTC timestamp of the event |
agent_id | string | ID of the agent that triggered the alert |
session_id | string | JWT session ID (jti claim) |
severity | string | low / medium / high / critical |
alert_type | string | e.g. dlp_violation, anomaly_detected, policy_deny |
tool_name | string | MCP tool that was called |
mcp_server | string | Target MCP server ID |
action | string | MCP method (e.g. tools/call) |
resource | string | Resource path or identifier |
policy_result | string | allow / deny / escalate |
policy_reason | string | Human-readable reason from the policy engine |
dlp_findings | array | DLP matches (pattern, severity, field) |
behavioral_score | number | Anomaly score 0–1 (higher = more anomalous) |
latency_ms | number | Total proxy latency in milliseconds |
Request Signing
Every webhook request includes a signature header:
X-Behavry-Signature: sha256=<hex>
The signature is an HMAC-SHA256 of the raw request body, using BEHAVRY_WEBHOOK_SECRET as the key.
Verification (Python)
import hashlib
import hmac
def verify_behavry_signature(body: bytes, signature_header: str, secret: str) -> bool:
"""Return True if the signature is valid."""
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
expected_header = f"sha256={expected}"
return hmac.compare_digest(signature_header, expected_header)
# Usage in a Flask webhook receiver:
@app.route("/webhook", methods=["POST"])
def handle_webhook():
sig = request.headers.get("X-Behavry-Signature", "")
if not verify_behavry_signature(request.data, sig, WEBHOOK_SECRET):
return "Unauthorized", 401
payload = request.get_json()
# ... process payload
return "OK", 200
Verification (Node.js)
const crypto = require('crypto');
function verifySignature(body, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}
Slack Integration
Use Slack's Incoming Webhooks to route Behavry alerts to a channel.
- Go to https://api.slack.com/apps → Create an app → Incoming Webhooks → Enable → Add to Workspace
- Copy the webhook URL (starts with
https://hooks.slack.com/services/...) - Set in your
.env:BEHAVRY_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx
BEHAVRY_WEBHOOK_SECRET=any-string # Slack doesn't verify HMAC, but set it anyway
BEHAVRY_WEBHOOK_MIN_SEVERITY=high - Restart the backend
Note: Slack Incoming Webhooks expect a specific
{"text": "..."}payload format. Behavry sends the raw alert JSON. You can use a small Lambda or proxy to reformat, or use a Slack workflow trigger instead of an Incoming Webhook for richer formatting.
PagerDuty Integration
Use PagerDuty's Events API v2:
BEHAVRY_WEBHOOK_URL=https://events.pagerduty.com/v2/enqueue
BEHAVRY_WEBHOOK_SECRET=your-pagerduty-integration-key
BEHAVRY_WEBHOOK_MIN_SEVERITY=critical
Note: PagerDuty expects a specific payload format. A thin adapter is needed to map Behavry's alert payload to PagerDuty's
routing_key,event_action, andpayloadstructure.
Reliability
- Behavry makes one attempt to deliver to each endpoint.
- On any failure (non-2xx, timeout, network error), it waits 2 seconds and retries once.
- If the retry also fails, the event is logged as
Webhook delivery failed(warning level) and dropped. - Webhook delivery is fire-and-forget — failures never block the event pipeline, the dashboard, or the audit log.
- There is no built-in persistent queue or dead-letter storage. For high-reliability delivery, put a queue (Redis, SQS, etc.) in front of your webhook receiver.
Testing
Trigger a test webhook by causing a critical DLP violation:
# Run the exfiltration threat demo (creates DLP violations)
python demos/simulators/threat_exfiltration.py
Or use the evaluate endpoint to test a policy, then check your webhook receiver for the delivery.
You can also use webhook.site as a temporary receiver to inspect payloads during setup:
BEHAVRY_WEBHOOK_URL=https://webhook.site/your-unique-id
BEHAVRY_WEBHOOK_SECRET=test-secret
BEHAVRY_WEBHOOK_MIN_SEVERITY=low # low threshold = receive all alerts during testing