---
title: Filter Events deliveries
description: >-
  Use triggers to narrow which changes qualify a delivery, and query_filter to
  suppress deliveries based on query results.
source_url:
  html: 'https://shopify.dev/docs/apps/build/events/delivery-filtering'
  md: 'https://shopify.dev/docs/apps/build/events/delivery-filtering.md'
---

# Filter Events deliveries

**Developer preview:**

Events is in developer preview on the [`unstable`](https://shopify.dev/docs/api/usage/versioning#making-requests-to-an-api-version) API version, available today for a [subset of topics](https://shopify.dev/docs/api/events). Use it for early testing ahead of a stable release and broader topic coverage. For topics not yet supported, use webhooks alongside Events in the same `shopify.app.toml`. As Events expands topic coverage, it will become the primary subscription mechanism.

By default, a subscription fires on any [qualifying change](https://shopify.dev/docs/apps/build/events#how-it-works) to the topic.

Delivery filtering lets you narrow that without changing your topic or actions. Two mechanisms run in sequence: `triggers` gates at the change level before any query runs, and `query_filter` gates afterward on the query result.

![Illustration of Events pipeline and how configuration gates deliveries with filters.](https://shopify.dev/assets/assets/images/apps/webhooks-events/events-gates-filters-1PuWpSWX.png)

***

## Triggers

`triggers` is an optional array of field paths on the subscribed `topic`.

When there's no `triggers` array, any field change that can fire a delivery will fire a delivery:

## Without triggers

##### shopify.app.toml

```toml
[events]
api_version = "unstable"

[[events.subscription]]
handle = "product-updates"
topic = "Product"
actions = ["update"]

uri = "https://your-app.example.com/events"
```

##### {} Response

```json
{
  "topic": "Product",
  "action": "update",
  "handle": "product-updates",
  "fields_changed": [
    "product[id: 'gid://shopify/Product/123'].title"
  ],
  "query_variables": {
    "productId": "gid://shopify/Product/123"
  }
}
```

Adding `triggers` narrows deliveries to changes on those paths:

## With triggers

##### shopify.app.toml

```toml
[events]
api_version = "unstable"

[[events.subscription]]
handle = "product-updates"
topic = "Product"
actions = ["update"]

triggers = [
    "product.variants.price",
    "product.variants.barcode"
]

uri = "https://your-app.example.com/events"
```

##### {} Response

```json
{
  "topic": "Product",
  "action": "update",
  "handle": "product-updates",
  "fields_changed": [
    "product[id: 'gid://shopify/Product/123'].variants[id: 'gid://shopify/ProductVariant/456'].price"
  ],
  "query_variables": {
    "productId": "gid://shopify/Product/123",
    "variantsId": "gid://shopify/ProductVariant/456"
  }
}
```

### Constraints

Each entry in `triggers` must be a supported field path for the subscribed `topic`:

* **Validation**: Shopify validates paths per topic at deploy time. An invalid path prevents the subscription from being created. Always use the [Events reference](https://shopify.dev/docs/api/events) as the source of valid trigger paths, not the GraphQL schema alone.
* **Derived fields**: Derived fields (for example, [`product.compareAtPriceRange`](https://shopify.dev/docs/api/admin-graphql/latest/objects/Product#field-Product.fields.compareAtPriceRange)) aren't valid triggers, because they don't have an independent change signal in the pipeline (it's derived from the highest and lowest product variant's compare-at prices).
* **Deprecated fields**: Deprecated fields can still be valid trigger paths. Deprecation signals that a field will eventually be removed from the API, but it doesn't remove the change signal from the pipeline. If both old and new field names exist (for example `bodyHtml` and `descriptionHtml`), changes to either can trigger. Check the [Events reference](https://shopify.dev/docs/api/events) to confirm which deprecated paths remain supported.

`triggers` only applies to `update` and is evaluated after `topic` and `actions` have matched. When multiple paths are listed, any one matching path qualifies the delivery.

***

## Query filters

`query_filter` runs after the [`query`](https://shopify.dev/docs/apps/build/events/delivery-structure#custom-queries) response exists. It decides whether to send a delivery using current values from the `query` output, not change deltas. It supports AND and OR combinations.

## With query\_filter

##### shopify.app.toml

```toml
[events]
api_version = "unstable"

[[events.subscription]]
handle = "product-price-change"
topic = "Product"
actions = ["update"]

triggers = [
    "product.variants.price"
]

uri = "https://your-app.example.com/events"

query = """
  query price_change($productId: ID!, $variantsId: ID!) {
    productVariant(id: $variantsId) {
      id
      price
    }
    product(id: $productId) {
      id
      title
      status
    }
  }
"""

query_filter = "product.status:'ACTIVE' AND productVariant.price:>100"
```

##### {} Response

```json
{
  "topic": "Product",
  "action": "update",
  "handle": "product-price-change",
  "data": {
    "productVariant": {
      "id": "gid://shopify/ProductVariant/456",
      "price": "129.99"
    },
    "product": {
      "id": "gid://shopify/Product/123",
      "title": "Widget",
      "status": "ACTIVE"
    }
  },
  "fields_changed": [
    "product[id: 'gid://shopify/Product/123'].variants[id: 'gid://shopify/ProductVariant/456'].price"
  ],
  "query_variables": {
    "variantsId": "gid://shopify/ProductVariant/456",
    "productId": "gid://shopify/Product/123"
  }
}
```

Without `query_filter`, the subscription above would deliver for every qualifying change regardless of the current field values. A price change on an inactive product would still fire. Adding the `query_filter` restricts deliveries to only variant price changes on active products.

### Constraints

Each `query_filter` expression must reference fields your `query` returns using the right syntax:

* **Query root**: Filter paths must start from the same root as your `query`. If your query starts at `productVariant`, the filter path must be `productVariant.price`, not `product.variants.price`.
* **Limitations**: `query_filter` can't express previous-value vs. new-value comparisons, reference fields not returned by your `query`, or replace multi-step business logic that belongs in your app.
* **Syntax**: There must be no space between `:` and the field value. `product.status:'ACTIVE'` is valid; `product.status: 'ACTIVE'` fails. Shopify validates the filter expression at deploy time.

***

## Example

Suppose you want your app to be notified only when a variant's price or compare-at price changes on an active product. Without filtering, your subscription fires for every field change on every product update, including title, tags, and status changes you don't care about.

The following subscription uses `triggers` to gate on price-related changes, a `query` to fetch current values, and `query_filter` to suppress deliveries for inactive products:

## Product price change

##### shopify.app.toml

```toml
[events]
api_version = "unstable"

[[events.subscription]]
handle = "product-price-change"
topic = "Product"
actions = ["update"]

triggers = [
  "product.variants.price",
  "product.variants.compareAtPrice"
]

uri = "https://your-app.example.com/events"

query = """
  query price_change($productId: ID!, $variantsId: ID!) {
    productVariant(id: $variantsId) {
      id
      price
      compareAtPrice
    }
    product(id: $productId) {
      id
      title
      status
    }
  }
"""

query_filter = "product.status:'ACTIVE' AND productVariant.price:>100"
```

##### {} Response

```json
{
  "topic": "Product",
  "action": "update",
  "handle": "product-price-change",
  "data": {
    "productVariant": {
      "id": "gid://shopify/ProductVariant/456",
      "price": "129.99",
      "compareAtPrice": "149.99"
    },
    "product": {
      "id": "gid://shopify/Product/123",
      "title": "Widget",
      "status": "ACTIVE"
    }
  },
  "fields_changed": [
    "product[id: 'gid://shopify/Product/123'].variants[id: 'gid://shopify/ProductVariant/456'].price"
  ],
  "query_variables": {
    "variantsId": "gid://shopify/ProductVariant/456",
    "productId": "gid://shopify/Product/123"
  }
}
```

This subscription delivers only when a variant's price or compare-at price changes on a product that is currently active and priced above $100.

***

## Next steps

* [Delivery structure](https://shopify.dev/docs/apps/build/events/delivery-structure): Payload fields, headers, and large payloads.
* [Verify deliveries](https://shopify.dev/docs/apps/build/events/verify-deliveries): HMAC verification and deduplication.

***
