Both are per gateway and both live in the same editor: Destinations tab → Edit routing on the row.
Filter rules
Section titled “Filter rules”A rule addresses one value in the request, compares it, and the group decides how the rules combine.
{ "operator": "and", "rules": [ { "path": "body.type", "op": "eq", "value": "invoice.paid" }, { "path": "body.data.object.amount", "op": "gt", "value": 5000 } ]}A path starts with one of two roots and then walks the JSON:
| Root | Reads |
|---|---|
body. | The payload, when it is JSON. |
headers. | The inbound headers. Names are lowercase. |
body.data.object.id, headers.x-github-event. A path with any other root
never matches.
Operators
Section titled “Operators”| Operator | True when |
|---|---|
eq | The value equals the rule’s value. |
ne | It differs, or the path is absent. |
contains | A string contains the substring, or an array contains the value. |
exists | The path resolves to anything, including null. |
gt / lt | Both sides are numbers and compare that way. |
in | The value is one of the array in the rule. |
Groups
Section titled “Groups”The top level has an operator of and or or. A rule entry can itself be a
group, giving one level of nesting — enough for “A and (B or C)”:
{ "operator": "and", "rules": [ { "path": "headers.x-github-event", "op": "eq", "value": "push" }, { "operator": "or", "rules": [ { "path": "body.ref", "op": "eq", "value": "refs/heads/main" }, { "path": "body.ref", "op": "eq", "value": "refs/heads/release" } ] } ]}Nesting deeper is rejected when you save.
What a filtered event looks like
Section titled “What a filtered event looks like”A non-matching event is stored, and its delivery for that gateway is recorded as
filtered. It is visible in the event log, was never sent, and is not retried.
Other gateways on the same source are unaffected.
An empty filter matches everything. A payload that is not valid JSON makes every
body. rule fail to match, so filter on headers when you expect non-JSON
bodies.


Transformations
Section titled “Transformations”Applied after the filter and before the authentication headers.
Headers
Section titled “Headers”Transformations operate on the headers Webhooker is about to send — the content type copied from the event, plus the destination’s fixed headers — not on the sender’s own headers, which are not forwarded (see HTTP headers).
- Set headers — add a header, or overwrite it if it already exists.
- Rename headers — change the name of a header already in the outbound set, keeping its value.
- Remove headers — drop a header from the outbound set, for example the content type or a fixed header that one endpoint must not receive.
Header names are matched case-insensitively. Set values support placeholders:
| Placeholder | Becomes |
|---|---|
{{event.id}} | The event id. |
{{source.name}} | The source’s name. |
{{timestamp}} | The delivery’s Unix timestamp. |
Merge body fields takes a JSON object and merges it into the top level of a JSON payload. Existing keys with the same name are overwritten, everything else is kept, and values support the same placeholders.
{ "received_via": "webhooker", "trace_id": "{{event.id}}" }A body that is not valid JSON is forwarded byte for byte; header rules still apply.
Order of operations
Section titled “Order of operations”- Base headers: content type, then the destination’s fixed headers.
- Transformation: renames, removals, then sets.
- Body merge.
- Authentication headers.
Because authentication comes last, a transformation cannot overwrite or remove the signature headers — so a broken transformation can never quietly turn a signed delivery into an unsigned one.