Webhook Integration
Send detection events to your own endpoints for custom processing, SIEM integration, or automation workflows.
Use Cases
Section titled “Use Cases”- Forward events to a SIEM WebDecoy has no dedicated integration for (for example Elastic)
- Trigger custom automation workflows
- Relay events into incident management through your own handler (WebDecoy sends its own payload format, not a vendor-specific one)
- Log to your own database
- Integrate with internal security tools
- Build custom dashboards
Creating a Webhook
Section titled “Creating a Webhook”-
Go to Integrations → Webhooks
-
Click Add Webhook
-
Configure:
Field Description Name Friendly name for the webhook Webhook URL Your endpoint URL. Use HTTPS; private, loopback and localhost addresses are refused -
Configure triggers and filters:
Setting Description All Detections Every new detection High Risk Detections (Score ≥ 80) Only detections whose bot_scoreis 80 or higherRule Enforced Events When a response action enforced successfully against the detection Minimum Score (optional) Skip detections below this unified score Filter by Decoys (optional) Only send detections from the selected decoys -
Click Create Webhook. The signing secret is generated for you and shown once; copy it then. You can regenerate it later from the webhook card.
Webhook Payload
Section titled “Webhook Payload”Your endpoint receives JSON payloads via POST request. Illustrative example (empty fields are omitted):
{ "event": "detection", "timestamp": "2026-07-15T10:30:00Z", "detection": { "id": "5d0c8f1e-...", "decoy_id": "c2e9f6a0-...", "organization_id": "3f1c2a9e-...", "ip_address": "192.168.1.100", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...", "bot_score": 92, "timestamp": "2026-07-15T10:30:00Z", "rules_enforced": false }}The payload is deliberately compact. Fetch the full enriched detection
(geo, signals, MITRE mapping, threat category) from
GET /organizations/{org}/detections/{id} using the detection.id.
metadata is present only on rule_enforced events (rules_enforced, plus enforcement_error when enforcement failed) and on test events ({"test": true}). detection.source is set only on test events ("test").
Event Types
Section titled “Event Types”| Event | Description |
|---|---|
detection |
New detection recorded |
high_risk_detection |
Detection with a unified score or bot_score of 80 or more |
rule_enforced |
Reserved for a rule enforced against the detection. The detection pipeline does not send it today, so subscribe to detection or high_risk_detection |
test |
Sent by the Test button when configuring the webhook |
Request Headers
Section titled “Request Headers”Every webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type |
application/json |
X-WebDecoy-Signature |
HMAC-SHA256 signature of the raw body (sha256=<hex>) |
X-WebDecoy-Event |
Event type (e.g., detection) |
X-WebDecoy-Delivery |
Unique delivery ID |
User-Agent |
WebDecoy-Webhook/1.0 |
Signature Verification
Section titled “Signature Verification”Verify webhooks are from WebDecoy using the signature header:
X-WebDecoy-Signature: sha256=abc123def456...Verification in Node.js
Section titled “Verification in Node.js”const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(payload, 'utf8') .digest('hex');
return `sha256=${expected}` === signature;}
// Express middlewareapp.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-webdecoy-signature']; const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); }
const event = JSON.parse(payload); // Process event...
res.status(200).send('OK');});Verification in Python
Section titled “Verification in Python”import hmacimport hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool: expected = hmac.new( secret.encode('utf-8'), payload, hashlib.sha256 ).hexdigest()
return f'sha256={expected}' == signature
# Flask example@app.route('/webhook', methods=['POST'])def webhook(): signature = request.headers.get('X-WebDecoy-Signature') payload = request.get_data()
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET): return 'Invalid signature', 401
event = request.get_json() # Process event...
return 'OK', 200Verification in Go
Section titled “Verification in Go”package main
import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "io" "net/http")
func verifySignature(payload []byte, signature, secret string) bool { mac := hmac.New(sha256.New, []byte(secret)) mac.Write(payload) expected := "sha256=" + hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(expected), []byte(signature))}
func webhookHandler(w http.ResponseWriter, r *http.Request) { payload, _ := io.ReadAll(r.Body) signature := r.Header.Get("X-WebDecoy-Signature")
if !verifySignature(payload, signature, webhookSecret) { http.Error(w, "Invalid signature", http.StatusUnauthorized) return }
// Process event... w.WriteHeader(http.StatusOK)}Delivery Semantics
Section titled “Delivery Semantics”Each event is delivered once. There are currently no automatic retries. Your endpoint must respond within 10 seconds; a non-2xx response or a timeout is recorded as a failed delivery (visible on the webhook’s stats in the dashboard). Design your handler to return 200 immediately and process asynchronously.
Best Practices
Section titled “Best Practices”- ✅ Verify signatures in production
- ✅ Return 200 quickly - process events asynchronously
- ✅ Handle duplicates - deduplicate on
detection.id(each delivery gets a newX-WebDecoy-DeliveryID) - ✅ Log failures for debugging
- ✅ Use HTTPS
Don’ts
Section titled “Don’ts”- ❌ Process events synchronously (causes timeouts)
- ❌ Skip signature verification
- ❌ Return errors for events you want to ignore
- ❌ Expose webhook URL publicly
Recommended Architecture
Section titled “Recommended Architecture”WebDecoy Webhook │ ▼Your Webhook Endpoint │ ├── Verify signature ├── Return 200 immediately └── Queue event for processing │ ▼ Background Worker │ ├── Process event ├── Forward to SIEM └── Trigger automationTesting Webhooks
Section titled “Testing Webhooks”Test from WebDecoy
Section titled “Test from WebDecoy”- Go to Integrations → Webhooks
- Find your webhook
- Click Test
- A test event (
event: "test") is sent to your endpoint - The result (success or the HTTP status) is shown, and the webhook’s stats update
Local Development
Section titled “Local Development”Use a tunneling service to test locally:
# Using ngrokngrok http 3000
# Your webhook URL becomes:# https://abc123.ngrok.io/webhookTroubleshooting
Section titled “Troubleshooting”Webhook Not Receiving Events
Section titled “Webhook Not Receiving Events”- Verify webhook is enabled
- Check URL is correct and accessible
- Ensure the host resolves to a public address (private and local addresses are refused)
- Check trigger settings match expected events
- Check the webhook card’s failure count and last error
Signature Verification Failing
Section titled “Signature Verification Failing”- Ensure you’re using the raw request body (not parsed JSON)
- Check secret matches exactly (no extra whitespace)
- Verify encoding is UTF-8
- Check signature header name is correct
Timeouts
Section titled “Timeouts”- Return 200 before processing
- Move heavy processing to background jobs
- Increase endpoint timeout if possible
Next Steps
Section titled “Next Steps”- Cloudflare - Block actors at your Cloudflare WAF
- Slack - Real-time notifications
- Overview - All integrations