Destination Filters

Destination filters allow tenants to selectively receive only events that match specific criteria. Instead of receiving all events for subscribed topics, a destination can define a filter to route events based on their properties.

How Filtering Works

When an event is published, Outpost evaluates each destination's filter against the event. A destination receives an event only if:

  1. The destination is enabled
  2. The event's topic matches one of the destination's subscribed topics
  3. The event matches the destination's filter (or no filter is defined)

Filters are optional — destinations without filters receive all events matching their topics.

Event Structure

Filters operate on the five top-level event properties:

PropertyDescription
idThe event ID
topicThe event topic
timeEvent timestamp (RFC 3339)
metadataAdditional event metadata
dataThe event payload

Exact Match

Match a field value exactly:

{ "data": { "status": "paid" } }

Multiple conditions are implicitly combined with AND:

{ "data": { "status": "paid", "currency": "usd" } }

Nested objects are supported:

{ "data": { "customer": { "tier": "premium" } } }

Arrays match if they contain the specified value:

{ "data": { "tags": "urgent" } }

This matches events like { "tags": ["urgent", "support"] }.

Operators

Comparison

OperatorDescription
$eqEquals
$neqNot equals
$gtGreater than
$gteGreater than or equal
$ltLess than
$lteLess than or equal
{ "data": { "amount": { "$gte": 100, "$lt": 500 } } }

Membership

OperatorDescription
$inValue in array, or substring match
$ninValue not in array
{ "data": { "status": { "$in": ["pending", "processing"] } } }

String

OperatorDescription
$startsWithString starts with value
$endsWithString ends with value
{ "data": { "email": { "$endsWith": "@example.com" } } }

Existence

OperatorDescription
$existtrue if field exists, false if it doesn't
{ "data": { "deleted_at": { "$exist": false } } }

Logical Operators

$or — Match any condition

{
  "$or": [
    { "data": { "status": "paid" } },
    { "data": { "status": "shipped" } }
  ]
}

$and — Explicit AND

Useful when you need multiple conditions that can't be nested:

{
  "$and": [
    { "data": { "status": "active" } },
    { "data": { "amount": { "$gte": 100 } } }
  ]
}

$not — Negate a condition

{
  "data": { "status": { "$not": { "$in": ["cancelled", "refunded"] } } }
}

Common Examples

Filter by metadata:

{ "metadata": { "source": "api" } }

High-value orders only:

{ "data": { "amount": { "$gte": 1000 } } }

Filter by time range:

{
  "time": {
    "$gte": "2025-01-01T00:00:00Z",
    "$lt": "2025-02-01T00:00:00Z"
  }
}

Setting a Filter via API

Filters are set in the filter field when creating or updating a destination:

curl 'https://api.outpost.hookdeck.com/2025-07-01/tenants/<TENANT_ID>/destinations' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data '{
  "type": "webhook",
  "topics": ["orders"],
  "config": { "url": "https://example.com/webhooks" },
  "filter": {
    "data": { "status": { "$in": ["paid", "shipped"] } }
  }
}'

To remove a filter, set it to an empty object:

curl --request PATCH \
'https://api.outpost.hookdeck.com/2025-07-01/tenants/<TENANT_ID>/destinations/<DESTINATION_ID>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data '{ "filter": {} }'

Enabling Filters in the Portal

Destination filters are disabled in the tenant portal by default. You can enable them by:

Toggle on the "Destination filter" setting in Hookdeck User Portal settings, or enable the equivalent value through the Config API.

Set the PORTAL_ENABLE_DESTINATION_FILTER configuration variable to true.