Filter webhooks payload
Learn how to write custom payload-filters for your triggers.
For custom webhooks, Dust cannot generate a filter automatically for triggers based on your natural language description. To filter the incoming calls, you'll need to write a filter using a custom syntax.
This syntax is described below. To use it and write your filters, you'll need to know the payload that the webhook source will send.
Webhook Filter Expression Language
Overview
The Webhook Filter Expression is a declarative language for filtering webhook payloads based on their JSON content. Filter expressions evaluate to either true (the webhook should be processed) or false (the webhook should be ignored).
The language uses Lisp-style S-expression syntax, where expressions are written as parenthesized prefix notation: (operator arguments...).
Syntax Basics
S-Expression Format
All expressions follow the pattern: (operator argument1 argument2 ...)
- Parentheses: Every expression is wrapped in parentheses
- Prefix notation: The operator comes first, followed by its arguments
- Whitespace: Arguments are separated by whitespace (spaces, tabs, newlines are all valid)
Data Types
- Strings: Must be enclosed in double quotes:
"value" - Numbers: Written without quotes:
42,3.14 - Booleans:
trueorfalse(no quotes) - Lists: Space-separated values in parentheses:
("option1" "option2" "option3")
Field Access
Fields are referenced using dot notation to traverse the JSON payload structure:
- Top-level field:
"action" - Nested field:
"issue.state" - Deeply nested:
"pull_request.head.ref" - Array wildcards:
"tags.*.name"extracts thenameproperty from each object in thetagsarray
Example payload:
{
"action": "opened",
"issue": {
"number": 42,
"state": "open",
"labels": ["bug", "critical"]
},
"tags": [
{"name": "bug", "id": 1},
{"name": "feature", "id": 2}
]
}Field references:
"action"→"opened""issue.number"→42"issue.labels"→["bug", "critical"]"tags.*.name"→["bug", "feature"]
Operators
Logical Operators
and
andReturns true if all sub-expressions evaluate to true.
Syntax: (and expr1 expr2 ... exprN)
Example:
(and
(eq "action" "opened")
(eq "issue.state" "open"))
Returns true only if action is "opened" AND issue state is "open".
or
orReturns true if any sub-expression evaluates to true.
Syntax: (or expr1 expr2 ... exprN)
Example:
(or
(eq "action" "opened")
(eq "action" "edited"))
Returns true if action is either "opened" OR "edited".
not
notReturns the negation of its single sub-expression.
Syntax: (not expr)
Example:
(not (eq "issue.state" "closed"))
Returns true if issue state is NOT "closed".
Equality Operator
eq
eqChecks for exact equality between a field and a value.
Syntax: (eq "field.path" value)
Examples:
(eq "action" "opened") ; String comparison
(eq "issue.number" 42) ; Numeric comparison
(eq "issue.locked" true) ; Boolean comparison
String Operators
starts-with
starts-withChecks if a string field starts with a given prefix.
Syntax: (starts-with "field.path" "prefix")
Example:
(starts-with "pull_request.head.ref" "feature/")
Returns true if the branch name starts with "feature/".
Note: Only works on string fields.
Array Operators
has
hasChecks if an array contains a specific value.
Syntax: (has "field.path" value)
Examples:
(has "issue.labels" "bug")
Returns true if the labels array contains "bug".
For arrays of objects, use wildcard paths:
(has "tags.*.name" "important")
Returns true if any tag object has name equal to "important".
has-all
has-allChecks if an array contains all specified values.
Syntax: (has-all "field.path" (value1 value2 ... valueN))
Examples:
(has-all "issue.labels" ("bug" "critical"))
Returns true only if the labels array contains both "bug" AND "critical".
For arrays of objects:
(has-all "tags.*.name" ("bug" "feature"))
Returns true if tags include objects with both names.
has-any
has-anyChecks if an array contains at least one of the specified values.
Syntax: (has-any "field.path" (value1 value2 ... valueN))
Examples:
(has-any "issue.labels" ("bug" "enhancement"))
Returns true if the labels array contains either "bug" OR "enhancement" (or both).
For arrays of objects:
(has-any "tags.*.id" (1 2 3))
Returns true if any tag has id 1, 2, or 3.
Numeric Operators
gt (Greater Than)
gt (Greater Than)Checks if a numeric field is greater than a value.
Syntax: (gt "field.path" number)
Example:
(gt "issue.comments" 10)
Returns true if the issue has more than 10 comments.
gte (Greater Than or Equal)
gte (Greater Than or Equal)Checks if a numeric field is greater than or equal to a value.
Syntax: (gte "field.path" number)
Example:
(gte "pull_request.changed_files" 5)
Returns true if the PR changed 5 or more files.
lt (Less Than)
lt (Less Than)Checks if a numeric field is less than a value.
Syntax: (lt "field.path" number)
Example:
(lt "issue.comments" 3)
Returns true if the issue has fewer than 3 comments.
lte (Less Than or Equal)
lte (Less Than or Equal)Checks if a numeric field is less than or equal to a value.
Syntax: (lte "field.path" number)
Example:
(lte "pull_request.additions" 100)
Returns true if the PR added 100 lines or fewer.
Existence Operator
exists
existsChecks if a field exists and is not null or undefined.
Syntax: (exists "field.path")
Example:
(exists "issue.milestone")
Returns true if the issue has a milestone assigned.
Common Patterns
Not Equal
To express "not equal", combine not and eq:
(not (eq "issue.state" "closed"))
Has None
To check that an array contains none of the specified values:
(not (has-any "issue.labels" ("wontfix" "duplicate")))
Multiple Conditions
Combine conditions with and:
(and
(eq "action" "opened")
(eq "sender.login" "alice")
(has "issue.labels" "urgent"))
Any of Multiple Conditions
Use or for alternatives:
(or
(eq "sender.login" "alice")
(eq "sender.login" "bob")
(eq "sender.login" "charlie"))
Complete Examples
Example 1: Simple Filter
Requirement: Only process when action is "opened"
(eq "action" "opened")
Example 2: Multiple Conditions
Requirement: Process opened tickets from a specific user
(and
(eq "action" "opened")
(eq "sender.login" "alice"))
Example 3: Label-Based Routing
Requirement: Process issues that have either "bug" or "enhancement" label
(has-any "issue.labels" ("bug" "enhancement"))
Example 4: Complex Criteria
Requirement: Process opened PRs with more than 20 changed files
(and
(eq "action" "opened")
(gt "pull_request.changed_files" 20))
Example 5: Advanced Multi-Condition
Requirement: Process if:
- Action is "opened" by alice or bob with a critical/urgent label, OR
- Action is "labeled" with both "needs-review" and "backend" labels and more than 5 comments
(or
(and
(eq "action" "opened")
(or
(eq "sender.login" "alice")
(eq "sender.login" "bob"))
(has-any "issue.labels" ("critical" "urgent")))
(and
(eq "action" "labeled")
(has-all "issue.labels" ("needs-review" "backend"))
(gt "issue.comments" 5)))
Important Notes
Type Safety
- All operators perform type-safe comparisons
- Comparing incompatible types returns
false - Example:
(gt "issue.title" 5)returnsfalsebecause title is a string, not a number
Non-Existent Fields
- Non-existent or null fields return
falsefor all operations - Exception:
(not (exists "field"))returnstruefor non-existent fields
Empty Lists
- Empty lists in
has-allandhas-anyreturnfalse - Example:
(has-any "labels" ())always returnsfalse
Case Sensitivity
- Field paths are case-sensitive
"issue.State"and"issue.state"are different fields
Formatting
- Whitespace and newlines are ignored
- Use indentation for readability in complex expressions
Best Practices
- Keep it simple: Use the simplest expression that meets your requirements
- Be specific: Reference the most specific fields available to avoid false positives
- Test thoroughly: Verify your filter against sample payloads before deployment
- Use wildcards: Leverage
.*notation for arrays of objects instead of complex nested logic - Document complex filters: Add comments (outside the expression) explaining the logic for complex filters
- Prefer
has-anyover multipleorstatements:(has-any "labels" ("a" "b" "c"))is cleaner than(or (has "labels" "a") (has "labels" "b") (has "labels" "c"))
Testing Your Filters
When writing filters:
- Start with your JSON payload structure
- Identify the fields you need to check
- Write a simple expression and verify it works
- Incrementally add conditions
- Test edge cases (missing fields, empty arrays, etc.)
Example workflow:
Payload: {"action": "opened", "issue": {"labels": ["bug"]}}
Step 1: (eq "action" "opened") ✓
Step 2: (has "issue.labels" "bug") ✓
Step 3: (and (eq "action" "opened") (has "issue.labels" "bug")) ✓
Updated about 7 hours ago
