Skip to main content

Custom Webhook Integration

Overview

The Custom Webhook integration allows you to implement your own guardrails logic by providing a webhook endpoint that validates tool calls. When enabled, Webrix will send validation requests to your webhook at two critical stages: before tool execution (input validation) and after tool execution (output validation).

This gives you complete control over your security and compliance requirements, allowing you to:

  • Validate Tool Inputs: Screen tool arguments before they're executed to detect policy violations, sensitive data, or inappropriate requests
  • Validate Tool Outputs: Review tool responses before they reach users to ensure compliance and safety
  • Custom Business Logic: Implement organization-specific rules, rate limiting, content filtering, or any other custom validation logic
  • Observe and Debug: Use "Observe Only Mode" to test your webhook without blocking any calls

How It Works

When Custom Webhook guardrails are enabled, the flow works as follows:

  1. Input Stage (Before Execution):

    • User initiates a tool call through an AI client
    • Webrix sends a POST request to your webhook with tool metadata and arguments
    • Your webhook analyzes the request and responds with shouldBlock: true or shouldBlock: false
    • If blocked, the tool is not executed and an error is returned to the user
  2. Output Stage (After Execution):

    • Tool executes successfully (only if input stage allowed it)
    • Webrix sends a POST request to your webhook with the tool results
    • Your webhook analyzes the response and decides whether to block it
    • If blocked, the results are not returned to the user

Webhook Request Format

Your webhook will receive POST requests with the following JSON payload:

{
"action": "tool_call",
"stage": "input",
"user": {
"email": "[email protected]"
},
"mcpClient": "claude-desktop",
"integration": {
"slug": "slack-integration",
"name": "Slack",
"description": "Connect to Slack",
"connectorId": "slack",
"authType": "oauth2"
},
"tool": {
"name": "send_message",
"arguments": {
"channel": "#general",
"text": "Hello team!"
}
}
}

Request Fields

FieldTypeDescription
action"tool_call"Always "tool_call" (reserved for future actions)
stage"input" | "output""input" for pre-execution, "output" for post-execution
user.emailstringEmail of the user making the request
mcpClientstringThe AI client being used (e.g., "claude-desktop", "cursor")
integration.slugstringUnique identifier for the integration
integration.namestringHuman-readable integration name
integration.descriptionstringIntegration description
integration.connectorIdstringThe connector type
integration.authTypestringAuthentication method used
tool.namestringName of the tool being called
tool.argumentsobjectArguments passed to the tool
payloadanyThe data being sent (input stage) or received (output stage) from the tool

Output Stage Example

When stage is "output", the payload includes the tool's results:

{
"action": "tool_call",
"stage": "output",
"user": {
"email": "[email protected]"
},
"mcpClient": "claude-desktop",
"integration": {
"slug": "slack-integration",
"name": "Slack",
"description": "Connect to Slack",
"connectorId": "slack",
"authType": "oauth2"
},
"tool": {
"name": "get_channel_history",
"arguments": {
"channel": "#private-channel"
}
},
"payload": {
"messages": [
{
"user": "U123",
"text": "Confidential project discussion",
"ts": "1234567890.123456"
}
]
}
}

Webhook Response Format

Your webhook must respond with a JSON object:

{
"shouldBlock": false
}

Response Fields

FieldTypeDescription
shouldBlockbooleantrue to block the call, false to allow it

Response Time

  • Your webhook should respond within 2.5 seconds
  • If your webhook takes longer or is unreachable, Webrix will fail open (allow the call)
  • In "Observe Only Mode", responses are never waited for

Configuration

Prerequisites

Before setting up the Custom Webhook integration, ensure you have:

  1. A publicly accessible webhook endpoint (HTTPS required for production)
  2. The ability to process POST requests and respond within 2.5 seconds
  3. Admin access to your Webrix dashboard

Setup Instructions

Step 1: Implement Your Webhook

Create a webhook endpoint that:

  1. Accepts POST requests with the JSON payload described above
  2. Implements your validation logic
  3. Returns a JSON response with shouldBlock: true or shouldBlock: false
  4. Responds within 2.5 seconds

Example implementation (Node.js/Express):

app.post("/validate-tool-call", async (req, res) => {
const { action, stage, user, integration, tool, payload } = req.body

// Implement your custom validation logic
let shouldBlock = false

// Example: Block sensitive Slack channels
if (
integration.connectorId === "slack" &&
tool.arguments.channel === "#executive-only"
) {
shouldBlock = true
}

// Example: Check for PII in outputs
if (stage === "output" && containsPII(payload)) {
shouldBlock = true
}

// Respond quickly
res.json({ shouldBlock })
})

Step 2: Enable in Webrix

  1. Log in to your Webrix admin panel
  2. Navigate to SettingsAdvanced Security Settings
  3. Toggle on Enable Custom Webhook Integration
  4. Enter your webhook URL (e.g., https://your-domain.com/validate-tool-call)
  5. (Optional) Enable Observe Only Mode for testing
  6. Click Save Changes

Step 3: Test Your Integration

Start with Observe Only Mode enabled:

  1. Your webhook will receive real requests
  2. But responses will be ignored (no calls will be blocked)
  3. Use this to test your webhook logic and ensure it responds correctly
  4. Monitor your webhook logs to verify requests are being received

Once confident:

  1. Disable Observe Only Mode
  2. Your webhook responses will now control whether calls are blocked
  3. Monitor your webhook and Webrix logs for any issues

Observe Only Mode

Observe Only Mode is designed for testing and debugging:

  • Webhook requests are sent but responses are not awaited
  • No calls will ever be blocked, regardless of your webhook's response
  • Perfect for:
    • Initial testing of your webhook
    • Logging and analytics without affecting users
    • Debugging validation logic in production

When to Use Observe Only Mode

  • ✅ Testing a new webhook implementation
  • ✅ Monitoring tool usage without enforcement
  • ✅ Collecting data to build validation rules
  • ✅ Debugging issues without impacting users

When to Disable Observe Only Mode

  • ✅ Webhook is tested and working correctly
  • ✅ Validation logic is finalized
  • ✅ Ready to enforce guardrails in production

Use Cases

Content Filtering

Block tool calls containing specific keywords or patterns:

function shouldBlockContent(tool, payload, stage) {
const bannedWords = ["confidential", "secret", "password"]
const content =
stage === "input" ? JSON.stringify(tool.arguments) : JSON.stringify(payload)

return bannedWords.some((word) => content.toLowerCase().includes(word))
}

Rate Limiting

Limit tool calls per user:

const rateLimits = new Map()

function shouldBlockRateLimit(user) {
const now = Date.now()
const userCalls = rateLimits.get(user.email) || []

// Keep only calls from last hour
const recentCalls = userCalls.filter((t) => now - t < 3600000)

if (recentCalls.length >= 100) {
return true // Block if over 100 calls/hour
}

rateLimits.set(user.email, [...recentCalls, now])
return false
}

Business Hours Enforcement

Block tool calls outside business hours:

function shouldBlockOutsideBusinessHours() {
const now = new Date()
const hour = now.getHours()
const day = now.getDay()

// Block weekends and outside 9am-5pm
if (day === 0 || day === 6 || hour < 9 || hour >= 17) {
return true
}

return false
}

PII Detection

Scan outputs for personally identifiable information:

function containsPII(payload) {
const piiPatterns = [
/\b\d{3}-\d{2}-\d{4}\b/, // SSN
/\b\d{16}\b/, // Credit card
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/, // Email
]

const payloadStr = JSON.stringify(payload)
return piiPatterns.some((pattern) => pattern.test(payloadStr))
}

Best Practices

Performance

  • Respond Quickly: Your webhook has 2.5 seconds to respond
  • Use Caching: Cache validation results when appropriate
  • Async Processing: For complex logic, log async and respond quickly
  • Monitor Timeouts: Track how often your webhook times out

Security

  • Use HTTPS: Always use HTTPS for your webhook endpoint
  • Validate Requests: Consider adding request signature verification
  • Rate Limit: Protect your webhook from abuse
  • Secure Storage: Store any secrets or API keys securely

Reliability

  • Handle Errors: Implement proper error handling
  • Log Everything: Keep detailed logs for debugging
  • Monitor Uptime: Ensure your webhook is highly available
  • Fail Safe: Remember that timeouts/errors fail open (allow calls)

Testing

  • Start with Observe Only: Test thoroughly before blocking real calls
  • Test Both Stages: Verify both input and output validation
  • Test Edge Cases: Try various tool types and scenarios
  • Monitor Initially: Watch logs closely after enabling

Troubleshooting

Webhook Not Receiving Requests

  1. Verify URL: Ensure your webhook URL is correct and publicly accessible
  2. Check HTTPS: Confirm your endpoint uses HTTPS
  3. Test Endpoint: Use curl or Postman to test your endpoint
  4. Check Firewall: Ensure no firewall is blocking Webrix's requests

Calls Not Being Blocked

  1. Check Observe Only Mode: Ensure it's disabled if you want to block calls
  2. Verify Response: Confirm your webhook returns {"shouldBlock": true}
  3. Check Response Time: Ensure you respond within 2.5 seconds
  4. Review Logs: Check Webrix and webhook logs for errors

Timeouts or Slow Performance

  1. Optimize Logic: Reduce computation time in your webhook
  2. Use Caching: Cache validation results when possible
  3. Increase Resources: Scale up your webhook server if needed
  4. Consider Async: For heavy operations, log async and respond quickly

False Positives

  1. Review Logic: Check if validation rules are too strict
  2. Add Exceptions: Whitelist known-safe patterns
  3. Use Observe Only: Test refined logic before enforcing
  4. Collect Data: Use observe mode to gather real-world examples

Error Handling

When Webhook Fails

Webrix implements a fail-open strategy:

  • If your webhook is unreachable → Call is allowed
  • If your webhook times out → Call is allowed
  • If your webhook returns an error → Call is allowed
  • Only shouldBlock: true will block a call

This ensures that webhook issues don't break your MCP functionality.

In Observe Only Mode

  • All calls are always allowed
  • Webhook errors are logged but don't affect users
  • Perfect for testing without risk

FAQ

Can I use different logic for input vs output stages?

Yes! Check the stage field in the webhook payload to implement different validation logic for each stage.

What happens if my webhook is down?

Webrix will fail open (allow the call). This ensures your MCP remains functional even if your webhook has issues.

Can I block some users but not others?

Yes! Use the user.email field to implement user-specific rules.

Can I customize the error message shown to users?

Currently, blocked calls return a standard "Tool blocked by organization's guardrails" message. Custom messages may be added in future versions.

Does this affect all integrations?

Yes, when enabled, all tool calls across all integrations are validated through your webhook.

Can I disable the webhook temporarily?

Yes, simply toggle off "Enable Custom Webhook Integration" in the settings. Changes take effect immediately.

What's the difference between this and Active Fence?

  • Active Fence: Analyzes prompt text content for safety/compliance
  • Custom Webhook: Gives you full control to implement any validation logic you need
  • You can use both simultaneously for defense in depth

Example Implementations

Python (FastAPI)

from fastapi import FastAPI, Request
from pydantic import BaseModel

app = FastAPI()

class WebhookResponse(BaseModel):
shouldBlock: bool

@app.post("/validate-tool-call")
async def validate_tool_call(request: Request):
body = await request.json()

stage = body.get("stage")
tool = body.get("tool", {})
user = body.get("user", {})
user_email = user.get("email")

# Your validation logic here
should_block = False

# Example: Block specific users
if user_email == "[email protected]":
should_block = True

# Example: Block sensitive tool calls
if tool.get("name") == "delete_all_data":
should_block = True

return WebhookResponse(shouldBlock=should_block)

Go

package main

import (
"encoding/json"
"net/http"
)

type User struct {
Email string `json:"email"`
}

type WebhookRequest struct {
Action string `json:"action"`
Stage string `json:"stage"`
User User `json:"user"`
MCPClient string `json:"mcpClient"`
Integration map[string]interface{} `json:"integration"`
Tool map[string]interface{} `json:"tool"`
Payload interface{} `json:"payload,omitempty"`
}

type WebhookResponse struct {
ShouldBlock bool `json:"shouldBlock"`
}

func validateToolCall(w http.ResponseWriter, r *http.Request) {
var req WebhookRequest
json.NewDecoder(r.Body).Decode(&req)

// Your validation logic
shouldBlock := false

// Example logic
if req.Stage == "input" && req.Tool["name"] == "sensitive_operation" {
shouldBlock = true
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(WebhookResponse{ShouldBlock: shouldBlock})
}

func main() {
http.HandleFunc("/validate-tool-call", validateToolCall)
http.ListenAndServe(":8080", nil)
}

Additional Resources

Support

For additional support:

  • Webrix Support: Contact your Webrix administrator or support team
  • Documentation: Check the Webrix documentation for updates
  • Community: Join the Webrix community for tips and examples