Skip to main content

Filters

Filters are conditional routing rules that determine which webhooks should be processed by your Bridges and Destinations. Using filters, you can create sophisticated routing logic to send specific webhooks to specific destinations based on their content.

Overview

Filters evaluate webhook data against conditions you define. When all filter conditions match, the webhook is processed. When any condition fails, the webhook is skipped for that Bridge or Destination.

Key features:

  • Powerful query language: MongoDB-style operators for flexible matching
  • Nested field access: Query deep into JSON payloads using dot notation
  • Multiple data types: Numbers, strings, booleans, arrays, objects
  • Logical operations: AND, OR, NOT for complex conditions
  • Real-time testing: Validate filters before deploying
  • Performance optimized: Minimal impact on webhook delivery latency

When to Use Filters

Common use cases:

  • Event type routing: Send different event types to different destinations
  • Threshold-based alerts: Notify only when values exceed limits
  • Environment filtering: Process only production or staging webhooks
  • Conditional transformations: Apply transformations based on content
  • Error handling: Route failed events to special destinations
  • Priority routing: Direct high-priority events to faster queues
  • Geographic filtering: Route based on location data
  • Customer segmentation: Handle VIP vs standard customers differently

Filter Syntax

Filters use a JSON-based query language similar to MongoDB:

{
"field.path": {
"$operator": "value"
}
}

Basic Example

Only process webhooks where the event type is "payment.succeeded":

{
"filters": [
{
"body.type": {
"$eq": "payment.succeeded"
}
}
]
}

Multiple Conditions

All conditions must match (implicit AND):

{
"filters": [
{
"body.type": { "$eq": "payment.succeeded" }
},
{
"body.amount": { "$gte": 100 }
},
{
"body.currency": { "$eq": "USD" }
}
]
}

Field Paths

Access webhook data using dot notation:

Top-level fields:

{
"method": { "$eq": "POST" }
}

Nested fields:

{
"body.user.email": { "$eq": "[email protected]" }
}

Array elements:

{
"body.items[0].price": { "$gt": 100 }
}

Deep nesting:

{
"body.data.customer.subscription.plan.tier": { "$eq": "premium" }
}

Available Fields

Filters can access all parts of the webhook request:

  • method: HTTP method (GET, POST, PUT, etc.)
  • url: Request URL
  • headers.Header-Name: Request headers
  • body: Parsed JSON body or raw string
  • body.field.nested: Any field within the JSON body

Comparison Operators

Equality

$eq: Equals

{
"body.status": { "$eq": "active" }
}

$neq: Not equals

{
"body.status": { "$neq": "deleted" }
}

Numeric Comparisons

$gt: Greater than

{
"body.amount": { "$gt": 100 }
}

$gte: Greater than or equal

{
"body.amount": { "$gte": 100 }
}

$lt: Less than

{
"body.quantity": { "$lt": 10 }
}

$lte: Less than or equal

{
"body.quantity": { "$lte": 10 }
}

String Operators

$contains: String contains substring

{
"body.message": { "$contains": "error" }
}

$startsWith: String starts with prefix

{
"body.event_type": { "$startsWith": "payment" }
}

$endsWith: String ends with suffix

{
"body.email": { "$endsWith": "@company.com" }
}

$regex: Regular expression matching

{
"body.phone": { "$regex": "^\\+1[0-9]{10}$" }
}

Array Operators

$in: Value is in array

{
"body.status": { "$in": ["active", "pending", "processing"] }
}

$nin: Value is not in array

{
"body.status": { "$nin": ["deleted", "archived"] }
}

Existence Operator

$exist: Field exists or doesn't exist

{
"body.optional_field": { "$exist": true }
}
{
"body.deleted_at": { "$exist": false }
}

Logical Operators

$or: Any condition matches

{
"$or": [
{ "body.priority": { "$eq": "high" } },
{ "body.amount": { "$gte": 10000 } }
]
}

$and: All conditions match

{
"$and": [
{ "body.type": { "$eq": "payment" } },
{ "body.status": { "$eq": "succeeded" } },
{ "body.amount": { "$gte": 100 } }
]
}

$not: Condition does not match

{
"$not": {
"body.test": { "$eq": true }
}
}

Reference Operator

$ref: Compare two fields within the same payload

{
"body.updated_at": { "$ref": "body.created_at" }
}

This matches when updated_at equals created_at (new records).

Complex Filter Examples

Event Type Routing

Route different GitHub event types to different destinations:

{
"destinations": [
{
"name": "PR Notifications",
"type": "slack",
"webhook_url": "{{SLACK_PR_WEBHOOK}}",
"filters": [
{ "body.action": { "$in": ["opened", "reopened"] } },
{ "headers.X-GitHub-Event": { "$eq": "pull_request" } }
]
},
{
"name": "Issue Notifications",
"type": "slack",
"webhook_url": "{{SLACK_ISSUE_WEBHOOK}}",
"filters": [
{ "headers.X-GitHub-Event": { "$eq": "issues" } }
]
}
]
}

Threshold-Based Alerts

Alert for high-value transactions:

{
"filters": [
{
"body.type": { "$eq": "payment.succeeded" }
},
{
"$or": [
{ "body.amount": { "$gte": 100000 } },
{ "body.customer.vip": { "$eq": true } }
]
}
]
}

Environment-Specific Routing

Process only production events:

{
"filters": [
{
"$and": [
{ "body.environment": { "$eq": "production" } },
{ "body.test": { "$exist": false } }
]
}
]
}

Error Detection

Route failed events to error handling:

{
"filters": [
{
"$or": [
{ "body.status": { "$in": ["failed", "error", "rejected"] } },
{ "body.error_code": { "$exist": true } },
{ "body.success": { "$eq": false } }
]
}
]
}

Geographic Filtering

Route based on country:

{
"destinations": [
{
"name": "US Region",
"type": "http",
"url": "https://us-api.company.com/webhooks",
"filters": [
{ "body.country": { "$eq": "US" } }
]
},
{
"name": "EU Region",
"type": "http",
"url": "https://eu-api.company.com/webhooks",
"filters": [
{ "body.country": { "$in": ["GB", "FR", "DE", "IT", "ES"] } }
]
}
]
}

Customer Segmentation

Handle VIP customers differently:

{
"destinations": [
{
"name": "VIP Customer Queue",
"type": "http",
"url": "https://api.company.com/vip-queue",
"filters": [
{
"$or": [
{ "body.customer.tier": { "$eq": "premium" } },
{ "body.customer.lifetime_value": { "$gte": 50000 } }
]
}
]
}
]
}

Content-Based Filtering

Filter by message content:

{
"filters": [
{
"$and": [
{ "body.message": { "$contains": "urgent" } },
{ "body.priority": { "$gte": 5 } },
{ "$not": { "body.message": { "$contains": "test" } } }
]
}
]
}

Filter Placement

Bridge-Level Filters

Applied before any destinations are processed:

{
"name": "Production Events Only",
"filters": [
{ "body.environment": { "$eq": "production" } }
],
"destinations": [...]
}

Use case: Filter out unwanted webhooks entirely (test events, staging, etc.)

Behavior:

  • If filters don't match, webhook is rejected and not processed at all
  • Event is logged as "filtered out"
  • No destinations are attempted

Destination-Level Filters

Applied per destination:

{
"destinations": [
{
"name": "High Priority",
"type": "http",
"url": "https://api.company.com/high-priority",
"filters": [
{ "body.priority": { "$eq": "high" } }
]
},
{
"name": "All Events",
"type": "http",
"url": "https://api.company.com/all-events"
}
]
}

Use case: Route different webhooks to different destinations

Behavior:

  • If filters don't match, delivery to that specific destination is skipped
  • Other destinations are still attempted
  • Event is logged as "filtered for destination X"

Testing Filters

Via API

Test filter syntax before deploying:

curl -X POST https://api.hooklistener.com/api/v1/filters/test \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": [
{
"body.amount": { "$gte": 100 }
}
],
"data": {
"body": {
"amount": 150,
"currency": "USD"
}
}
}'

Response:

{
"match": true,
"details": {
"body.amount": {
"operator": "$gte",
"expected": 100,
"actual": 150,
"result": true
}
}
}

Via Dashboard

  1. Go to Bridges → Select Bridge → Edit
  2. Add or modify filters
  3. Click "Test Filters"
  4. Paste sample webhook payload
  5. See which filters match or fail

Best Practices

Performance

  1. Keep filters simple

    • Simpler filters execute faster
    • Avoid deeply nested logical operations
    • Use specific field paths
  2. Place selective filters early

    • Most selective conditions first
    • Filter out majority of unwanted webhooks quickly
    • Reduce unnecessary processing
  3. Use appropriate operators

    • $eq is faster than $regex
    • $in with small arrays is efficient
    • Avoid complex regular expressions

Maintainability

  1. Use descriptive conditions

    • Clear field names and values
    • Comment complex filter logic in documentation
    • Group related conditions logically
  2. Test thoroughly

    • Use filter test endpoint
    • Test with real webhook payloads
    • Verify edge cases
  3. Document filter intent

    • Explain why filters exist
    • Note expected behavior
    • Track changes to filter logic

Reliability

  1. Handle missing fields

    • Use $exist to check field presence
    • Consider default values
    • Don't assume fields always exist
  2. Account for data variations

    • Different event types have different structures
    • Use $or for acceptable variations
    • Test with multiple payload samples
  3. Avoid brittle filters

    • Don't rely on exact string matches if values might change
    • Use $contains or $startsWith when appropriate
    • Consider using $in for known value sets

Common Patterns

Multi-Stage Filtering

Bridge-level filter + destination-level filters:

{
"name": "Payment Processing",
"filters": [
{ "body.type": { "$startsWith": "payment" } }
],
"destinations": [
{
"name": "Successful Payments",
"filters": [
{ "body.status": { "$eq": "succeeded" } }
]
},
{
"name": "Failed Payments",
"filters": [
{ "body.status": { "$in": ["failed", "declined"] } }
]
}
]
}

Fallback Routing

Primary destination with filtered routing, fallback catches all:

{
"destinations": [
{
"name": "Premium Customers",
"filters": [
{ "body.customer.tier": { "$eq": "premium" } }
]
},
{
"name": "Standard Customers",
"filters": [
{ "body.customer.tier": { "$eq": "standard" } }
]
},
{
"name": "All Other Events"
// No filters - catches everything
}
]
}

Exclusion Filtering

Filter out unwanted events:

{
"filters": [
{
"$not": {
"body.event_type": { "$in": ["ping", "test", "health_check"] }
}
},
{
"body.environment": { "$neq": "development" }
}
]
}

Troubleshooting

Filters Not Matching Expected Webhooks

Check field paths:

# Verify the exact structure of your webhook
curl -X POST https://api.hooklistener.com/api/v1/filters/test \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filters": [],
"data": <paste-your-webhook-here>
}'

Common issues:

  • Incorrect field path: body.data.user.id vs body.user.id
  • Case sensitivity: body.Status vs body.status
  • Data type mismatch: "100" (string) vs 100 (number)
  • Missing fields: Field doesn't exist in all webhooks

Filters Matching Too Many Webhooks

Too broad conditions:

  • Use more specific operators
  • Add additional conditions (implicit AND)
  • Check for edge cases

Review event history:

  • Look at webhooks that incorrectly matched
  • Identify common patterns
  • Refine filter logic

Performance Issues

Complex filters:

  • Simplify logical operations
  • Reduce nesting depth
  • Split into multiple simpler filters

Regular expressions:

  • Use simpler operators when possible
  • Optimize regex patterns
  • Consider string operators instead

Filter Limitations

Not supported:

  • External API calls or database queries
  • Dynamic/computed filter values
  • Cross-webhook comparisons
  • Time-based filtering (use current payload data instead)

Workarounds:

  • Use transformations to enrich data before filtering
  • Add timestamp/date fields to payload
  • Pre-process webhooks before sending to Hooklistener

Next Steps

Now that you understand Filters, explore related features:

  1. Use Transformations to modify payloads before filtering
  2. Build Bridges to combine filters with routing
  3. Monitor Events to see filter results
  4. Track Issues when filters behave unexpectedly

Filters are a powerful tool for creating intelligent webhook routing logic. By combining filters with Sources, Destinations, and Transformations, you can build sophisticated automation workflows that handle different webhook types, priorities, and destinations with precision.