Skip to main content

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

FieldTypeDescription
event_typestringAlways "alert" for webhook deliveries
timestampISO 8601UTC timestamp of the event
agent_idstringID of the agent that triggered the alert
session_idstringJWT session ID (jti claim)
severitystringlow / medium / high / critical
alert_typestringe.g. dlp_violation, anomaly_detected, policy_deny
tool_namestringMCP tool that was called
mcp_serverstringTarget MCP server ID
actionstringMCP method (e.g. tools/call)
resourcestringResource path or identifier
policy_resultstringallow / deny / escalate
policy_reasonstringHuman-readable reason from the policy engine
dlp_findingsarrayDLP matches (pattern, severity, field)
behavioral_scorenumberAnomaly score 0–1 (higher = more anomalous)
latency_msnumberTotal 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.

  1. Go to https://api.slack.com/apps → Create an app → Incoming Webhooks → Enable → Add to Workspace
  2. Copy the webhook URL (starts with https://hooks.slack.com/services/...)
  3. 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
  4. 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, and payload structure.


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