Filters


Filters allow you to permit and route events conditionally based on the contents of their Headers, Body, Query, or Path.

The two most common uses of filters are:

  • Allowing only events with useful information to pass through to a destination.
  • Routing events to different destinations based on their contents, as in a fan out approach.

Filters utilize JSON, and support matching on any value (string, number, boolean, null), on nested objects, and on arrays. There is also support for special operators. For a full rundown of supported schema, see filter syntax.

Filters are applied to an event after a transformation by default, if one is defined. However, you can configure the order of transformations and filters in your connection rules. This allows you to run transformations before or after filters, depending on your use case.

How to Order Transformations & Filters ->

Learn how to control the order of transformations and filters in your connections.

Filter limitations

Filters were designed for JSON payloads. To filter non-JSON payloads, you can use a transformation to convert the payload to JSON. The transformation can be configured to run before the filter.

Basic, non-structured filters, such as partial or exact matches on strings, will function with XML payloads.

Filters syntax

Filters utilize JSON, and support matching on any value (string, number, boolean, null), on nested objects, and on arrays.

The following events would be permitted through the filter, based on their request bodies and respective filters.

Simple matches

Single value match

Data
Filter schema

Simple primitive match

Data
Filter schema

Nested object match

Data
Filter schema

Also valid:

Data
Filter schema

Array contains match

Data
Filter schema

Array contains (multiple) match

Array must contain all the specified values.

Data
Filter schema

Non-object match

To match on a string that is not part of an object structure (for example, to set a filter on a path), simply consider the string to be at the root of your filter:

Test data
Filter schema

Complex matches using operators

Operators allow for more complex matching strategies, beyond simple equivalency tests.

OperatorSupported TypeDescription
$gtenumber, stringGreater than or equal to
$gtnumber, stringGreater than
$ltnumber, stringLess than
$ltenumber, stringLess than or equal to
$eqarray, number, object, stringEqual (or deep equal)
$neqarray, number, object, stringNot Equal (or deep not equal)
$inarray, stringContains
$ninarray, stringDoes not contain
$startsWithstringStarts with text
$endsWithstringEnds with text
$orarrayArray of conditions to match
$andarrayArray of conditions to match
$ref<field>Reference a field
$existbooleanUndefined or not undefined
$notValid syntaxNegation

Value comparison operators

The following demonstrates a matching filter using the $lte (less than or equal to) operator.

Data
Filter schema

$or and $and operators

$or and $and evaluate matches across an array of values. The array of conditions can contain any other valid schema.

The following demonstrates a matching filter using the $or operator.

Data
Filter schema

The following demonstrates a matching filter using the $or operator. The data has hello: "world", which matches the first option in the $or array, so the event is Not Filtered — it matches and is forwarded.

Data
Filter schema

To see Filtered — the event filtered out, not forwarded — change the data to something that matches neither option (e.g. {"hello":"other"}).

Reference operator

$ref references other values in your JSON input when evaluating a match. The reference input must be a string representing the value path.

// Event request body
{
  "type": "example",
  "nested_object": {
    "hello": "world",
    "array": [1, 2, 3]
  }
}

// Example reference values
"$ref" : "type" // evaluates to "example"
"$ref" : "nested_object.hello" // evaluates to "world"
"$ref" : "nested_object.array[1]" // evaluates to 1
"$ref" : "nested_object.array[$index]" // evaluates to 1, 2, or 3 (depending on the current index)

The following demonstrates a matching filter using the $ref operator.

Data
Filter schema

You may also reference the current array index, instead of a specific index, using $index. Multiple indexes can be referenced when dealing with nested arrays.

Test if any of the elements in the array has the same value for updated_at and created_at. This will match because the 2nd element has the same date for both updated_at and created_at.

Data
Filter schema

$ref may also be used in conjunction with other operators.

Data
Filter schema

$exist operator

$exist requires a field to be undefined when false and array, number, object, string, boolean or null when true.

Data
Filter schema

Negation operator

$not negation of the schema.

Data
Filter schema

Examples using operators

The following examples show real-world filter patterns. For filter-out scenarios, we include both a Filtered (event filtered out, not forwarded) and Not Filtered (event matches and is forwarded) example to clarify the behavior.

Example 1 - Filter if property = value (nested)

Condition Don't allow "subtype" = "message_changed" OR "subtype" = "message_deleted"

This event is Filtered — it does not match and is filtered out (not forwarded) — because subtype is "message_changed":

Data
Filter schema

In contrast, this event is Not Filtered — it matches and is forwarded — because subtype is "message_sent", which is not in the filtered-out list:

Data
Filter schema

Example 2 - Two properties meet criteria with 'or' condition

Condition Only allow "subtype" = "message_changed" OR "subtype" = "message_deleted" AND "type"="message"

This event is Not Filtered — it matches and is forwarded — because type is "message" and subtype is "message_changed":

Data
Filter schema

This event is Filtered — it does not match and is filtered out (not forwarded) — because subtype is "message_sent", which is not in the match list:

Data
Filter schema

Example 3 - using $not and $or operators

Condition

  1. Allow "event" = "message"
  2. AND don't allow "team_id" = "team1" OR "team_id" = "team2"
  3. AND if "subtype" exists, don't allow "subtype" = "message_changed" OR "subtype" = "message_deleted"

This event is Filtered — it does not match and is filtered out (not forwarded) — because subtype is "message_changed":

Data
Filter schema

This event is Not Filtered — it matches and is forwarded — because subtype is "message_sent" and team_id is "team", which satisfy the conditions:

Data
Filter schema

Example 4 - using $not and $endsWith

Condition Don't allow where "subtype" ends with "message_changed"

This event is Filtered — it does not match and is filtered out (not forwarded) — because "some_message_changed" ends with "message_changed":

Data
Filter schema

This event is Not Filtered — it matches and is forwarded — because "message_sent" does not end with "message_changed":

Data
Filter schema

Create or add a filter

Filters are applied to a connection, just like any other rule. You can add a filter when creating a new connection or to an existing connection.

To version control your filters, consider creating or updating your connection using the API.

  1. Open Connections. Click the connection line.
  2. In Connection Rules, click Filter, then Editor.
  3. Select the tab for Body, Headers, Query, or Path and enter the schema in the right pane using filter syntax. The left pane shows a sample payload for reference.
  4. Click Test Filter to verify, then Save.
  5. Click Save again on the connection form to apply.

For a new connection, include the filter in your initial upsert:

hookdeck connection upsert my-connection \
  --source-name my-source --source-type WEBHOOK \
  --destination-name my-destination --destination-type HTTP \
  --destination-url https://api.example.com/webhooks \
  --rules-file rules.json

To add a filter to an existing connection, run upsert with your updated rules.json:

hookdeck connection upsert my-connection \
  --rules-file rules.json

Where rules.json contains:

[
  {
    "type": "filter",
    "body": {
      "type": "order.completed"
    }
  }
]

See the CLI Connection Commands reference for all available options.

New connection

PUT
/2025-07-01/connections
Request body example
JSON
{
  "name": "shopify-my-api",
  "source": {
    "name": "shopify"
  },
  "destination": {
    "name": "my-api",
    "config": {
      "url": "https://mock.hookdeck.com/example"
    }
  }
}
Response example
JSON
{
  "id": "web_nbbweTiOtzsm",
  "team_id": "tm_lbhzBKgFOUnB",
  "updated_at": "2026-01-14T13:36:41.634Z",
  "created_at": "2026-01-14T13:36:41.595Z",
  "paused_at": null,
  "name": "shopify-my-api",
  "rules": [],
  "description": null,
  "destination": {
    "id": "des_TU9ioCk5EHUU",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.263Z",
    "name": "my-api",
    "description": null,
    "type": "HTTP",
    "config": {
      "url": "https://mock.hookdeck.com/example",
      "rate_limit": null,
      "rate_limit_period": "second",
      "http_method": null,
      "path_forwarding_disabled": false,
      "auth": {},
      "auth_type": "HOOKDECK_SIGNATURE"
    },
    "disabled_at": null
  },
  "source": {
    "id": "src_qa5626p6y5o79b",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.226Z",
    "name": "shopify",
    "description": null,
    "type": "WEBHOOK",
    "config": {
      "allowed_http_methods": [
        "POST",
        "PUT",
        "PATCH",
        "DELETE"
      ],
      "custom_response": null
    },
    "url": "http://localhost:8787/qa5626p6y5o79b",
    "disabled_at": null,
    "authenticated": false
  },
  "disabled_at": null,
  "full_name": "shopify -> shopify-my-api"
}

Include the filter in the rules array when creating the connection:

curl -X PUT "https://api.hookdeck.com/2025-07-01/connections" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-connection",
    "source": { "name": "my-source" },
    "destination": { "name": "my-destination" },
    "rules": [
      {
        "type": "filter",
        "body": { "type": "order.completed" }
      }
    ]
  }'

Existing connection

PUT
/2025-07-01/connections/:id
Request body example
JSON
{
  "rules": [
    {
      "type": "retry",
      "count": 5,
      "interval": 3600000,
      "strategy": "linear"
    },
    {
      "body": {
        "type": "customer.subscription.updated"
      },
      "type": "filter"
    }
  ]
}
Response example
JSON
{
  "id": "web_FMKlTwAoGFRu",
  "team_id": "tm_lbhzBKgFOUnB",
  "updated_at": "2026-01-14T13:36:41.659Z",
  "created_at": "2026-01-14T13:36:06.347Z",
  "paused_at": null,
  "name": "shopify-orders",
  "rules": [
    {
      "type": "retry",
      "count": 5,
      "interval": 3600000,
      "strategy": "linear",
      "response_status_codes": null
    },
    {
      "body": {
        "type": "customer.subscription.updated"
      },
      "type": "filter"
    }
  ],
  "description": null,
  "destination": {
    "id": "des_TU9ioCk5EHUU",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.263Z",
    "name": "my-api",
    "description": null,
    "type": "HTTP",
    "config": {
      "url": "https://mock.hookdeck.com/example",
      "rate_limit": null,
      "rate_limit_period": "second",
      "http_method": null,
      "path_forwarding_disabled": false,
      "auth": {},
      "auth_type": "HOOKDECK_SIGNATURE"
    },
    "disabled_at": null
  },
  "source": {
    "id": "src_qa5626p6y5o79b",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.226Z",
    "name": "shopify",
    "description": null,
    "type": "WEBHOOK",
    "config": {
      "allowed_http_methods": [
        "POST",
        "PUT",
        "PATCH",
        "DELETE"
      ],
      "custom_response": null
    },
    "url": "http://localhost:8787/qa5626p6y5o79b",
    "disabled_at": null,
    "authenticated": false
  },
  "disabled_at": null,
  "full_name": "shopify -> shopify-orders"
}

Send a PUT request with the connection ID in the path and the updated rules array:

curl -X PUT "https://api.hookdeck.com/2025-07-01/connections/conn_xxx" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      {
        "type": "filter",
        "body": { "type": "order.completed" }
      }
    ]
  }'

From this point on, any events not matching the filter are disregarded by Hookdeck and recorded as ignored events. They will not appear in your list of events or generate delivery attempts. When a request produces only ignored events (e.g., because all connections filtered it out), it is a discarded request.

Edit a filter

Editing a filter changes the schema that determines an event's delivery eligibility.

  1. Open Connections. Click the connection line.
  2. Next to the filter rule, click Editor.
  3. Select the tab for Body, Headers, Query, or Path and update the schema in the right pane using filter syntax. The left pane shows a sample payload for reference.
  4. Click Test Filter to verify, then Save.
  5. Click Save again on the connection form to apply.
hookdeck connection upsert my-connection \
  --rules-file rules.json

Update the filter schema in your rules.json file. See the CLI Connection Commands reference for all available options.

PUT
/2025-07-01/connections/:id
Request body example
JSON
{
  "rules": [
    {
      "type": "retry",
      "count": 5,
      "interval": 3600000,
      "strategy": "linear"
    },
    {
      "body": {
        "type": "customer.subscription.updated"
      },
      "type": "filter"
    }
  ]
}
Response example
JSON
{
  "id": "web_FMKlTwAoGFRu",
  "team_id": "tm_lbhzBKgFOUnB",
  "updated_at": "2026-01-14T13:36:41.659Z",
  "created_at": "2026-01-14T13:36:06.347Z",
  "paused_at": null,
  "name": "shopify-orders",
  "rules": [
    {
      "type": "retry",
      "count": 5,
      "interval": 3600000,
      "strategy": "linear",
      "response_status_codes": null
    },
    {
      "body": {
        "type": "customer.subscription.updated"
      },
      "type": "filter"
    }
  ],
  "description": null,
  "destination": {
    "id": "des_TU9ioCk5EHUU",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.263Z",
    "name": "my-api",
    "description": null,
    "type": "HTTP",
    "config": {
      "url": "https://mock.hookdeck.com/example",
      "rate_limit": null,
      "rate_limit_period": "second",
      "http_method": null,
      "path_forwarding_disabled": false,
      "auth": {},
      "auth_type": "HOOKDECK_SIGNATURE"
    },
    "disabled_at": null
  },
  "source": {
    "id": "src_qa5626p6y5o79b",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.226Z",
    "name": "shopify",
    "description": null,
    "type": "WEBHOOK",
    "config": {
      "allowed_http_methods": [
        "POST",
        "PUT",
        "PATCH",
        "DELETE"
      ],
      "custom_response": null
    },
    "url": "http://localhost:8787/qa5626p6y5o79b",
    "disabled_at": null,
    "authenticated": false
  },
  "disabled_at": null,
  "full_name": "shopify -> shopify-orders"
}

Update the rules array in the request body to include the modified filter. See the Connections API reference.

Filters are updated immediately, and any events received going forward are tested against the new filter contents.

Delete a filter

Deleting a filter allows all events, regardless of content, to pass through a connection unimpeded.

  1. Open Connections. Click the connection line.
  2. Next to the filter rule, click the trash icon to remove it.
  3. Click Save on the connection form to apply.

Update your connection rules to remove the filter from the rules array:

hookdeck connection upsert my-connection \
  --rules-file rules.json

Ensure rules.json no longer includes the filter rule. See the CLI Connection Commands reference.

PUT
/2025-07-01/connections/:id
Request body example
JSON
{
  "rules": [
    {
      "type": "retry",
      "count": 5,
      "interval": 3600000,
      "strategy": "linear"
    },
    {
      "body": {
        "type": "customer.subscription.updated"
      },
      "type": "filter"
    }
  ]
}
Response example
JSON
{
  "id": "web_FMKlTwAoGFRu",
  "team_id": "tm_lbhzBKgFOUnB",
  "updated_at": "2026-01-14T13:36:41.659Z",
  "created_at": "2026-01-14T13:36:06.347Z",
  "paused_at": null,
  "name": "shopify-orders",
  "rules": [
    {
      "type": "retry",
      "count": 5,
      "interval": 3600000,
      "strategy": "linear",
      "response_status_codes": null
    },
    {
      "body": {
        "type": "customer.subscription.updated"
      },
      "type": "filter"
    }
  ],
  "description": null,
  "destination": {
    "id": "des_TU9ioCk5EHUU",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.263Z",
    "name": "my-api",
    "description": null,
    "type": "HTTP",
    "config": {
      "url": "https://mock.hookdeck.com/example",
      "rate_limit": null,
      "rate_limit_period": "second",
      "http_method": null,
      "path_forwarding_disabled": false,
      "auth": {},
      "auth_type": "HOOKDECK_SIGNATURE"
    },
    "disabled_at": null
  },
  "source": {
    "id": "src_qa5626p6y5o79b",
    "team_id": "tm_lbhzBKgFOUnB",
    "updated_at": "2026-01-14T13:36:41.615Z",
    "created_at": "2026-01-14T13:35:55.226Z",
    "name": "shopify",
    "description": null,
    "type": "WEBHOOK",
    "config": {
      "allowed_http_methods": [
        "POST",
        "PUT",
        "PATCH",
        "DELETE"
      ],
      "custom_response": null
    },
    "url": "http://localhost:8787/qa5626p6y5o79b",
    "disabled_at": null,
    "authenticated": false
  },
  "disabled_at": null,
  "full_name": "shopify -> shopify-orders"
}

Send a PUT request with the rules array omitting the filter rule. See the Connections API reference.

Try out a filter

Use the interactive filter tester below to experiment with Body, Headers, Path, and Query. Enter test data and a filter schema to see whether events would be allowed through or filtered out.

Filter tester

Loading...
Test data
Filter schema
Test data
Filter schema
Test data
Filter schema
Test data
Filter schema