Next Generation Events are now available in developer preview. Events give you field-level control over when a subscription fires, what data it carries, and what triggers each delivery, fixing three common friction points faced with traditional webhooks: over-delivery, fixed payloads, and no built-in signal for what changed.
Events are available today on the unstable API version for the Product and Customer topics, with more rolling out through 2026.
Anchor to Classic webhooks on ShopifyClassic webhooks on Shopify
Apps often need to react when merchant data changes in Shopify:
- A product information management app syncing product data to an external catalog.
- A loyalty app updating a customer profile when customer data changes.
- A fulfillment app reacting when an order changes.
Classic webhooks have solved this by giving apps a way to subscribe to a topic and receive a payload when that resource changes. For example, if your app wants to know when products change, you can subscribe to topic:
[webhooks]
api_version = "2026-07"
[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "/webhooks/products/update"
When a product updates, Shopify sends a delivery to your app. Your app gets a signal that something changed, but the signal is broader than the workflow your app is trying to run. Your handler still needs to read the payload and decide whether the delivery matters. For example, an app syncing prices to an external catalog is only interested when the prices of items change. Since the webhook fires on all product changes, including title, tags, status, and options, the app still needs to determine whether a delivery needs to be acted on or thrown away.
You can use to reduce the webhook payload to a point, but Shopify still sends a delivery for every qualifying :
[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "/webhooks/products/update"
include_fields = ["id", "status", "variants.id", "variants.price", "updated_at"]
You can also use filter to suppress deliveries based on the current values from a predetermined payload doesn't describe which field changed, and it doesn't prevent unrelated product updates from being delivered if the current payload still matches the filter:
[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "/webhooks/products/update"
include_fields = ["id", "status", "variants.id", "variants.price", "updated_at"]
filter = "status:active AND variants.price:>=10.00"
In this example, Shopify sends the webhook for any product update where the product is active and at least one variant currently has a price of 10.00 or more. A title edit, tag update, or option change can still pass the filter if the product already matches those conditions. If your app only wants price changes, then the handler still has work to do:
- Receive
deliveries that pass any configuredfilter. - Compare the current payload against previous state stored by your app.
- Discard deliveries where the product still matches the filter but the price didn't change.
- Make a follow-up GraphQL Admin API request if the webhook payload doesn't include all the data needed to respond.
- Run the sync after the app has proved that the delivery is relevant.
For workflows that care about a specific field changing, detection, state comparison, and follow-up fetching are pushed into the app.
Anchor to Introducing EventsIntroducing Events
Events keep the parts of Classic webhooks that make them useful and introduces new configuration to define what is relevant to your app.
Instead of subscribing to a classic webhook topic and figuring out whether a delivery matters afterwards, you define the topic, action, field-level triggers, payload shape, and delivery conditions in shopify.app.toml. Topics map to GraphQL Admin API objects, so a topic covers the entity you want to subscribe to and its owned data. Actions are standardized to create, update, and delete, and they describe the lifecycle event on the root entity. For example, adding a variant to a product is an update action on the Product topic; the action tells you the product changed, while triggers and tell you what changed inside it.
For the same price sync workflow, consider the following Events subscription:
[events]
api_version = "unstable"
[[events.subscription]]
handle = "price_sync"
topic = "Product"
actions = ["update"]
triggers = ["product.variants.price", "product.variants.compareAtPrice"]
uri = "/api/events"
query = """
query priceSync($productId: ID!, $variantsId: ID!) {
productVariant(id: $variantsId) {
id
price
compareAtPrice
sku
}
product(id: $productId) {
id
title
status
}
}
"""
query_filter = "product.status:'ACTIVE'"
This subscription moves the relevance check into Shopify in three places.
- First,
triggerspre-qualify which field changes can fire theupdatedelivery. In this case, a delivery qualifies only whenproduct.variants.priceorchanges. A product title edit, tag update, or option change doesn't qualify for this subscription. Triggers are optional; omitting them means every trigger that can fire an Event, will fire an Event - Second, you can design the payload you receive with
query, which is a standard GraphQL Admin API query. Shopify runs the query after the qualifying change and includes the result in the delivery'sdatafield. In this case, your handler receives the changed variant's price, compare-at price, and SKU, plus the product's title and status, without treating the delivery as a signal to make a second request. There's no fixed payload schema to work around; the response is shaped for the use case you configured. If you don't need data in the delivery, the query is optional and Shopify sends a thinner payload withand. - Third,
suppresses deliveries that don't match the current query result. In this example,product.status:'ACTIVE'means Shopify sends the delivery only when the product's current status is active. Any field you use inmust also exist in your query. Together,triggersandhelp the right deliveries reach your app before your endpoint is called.
When those conditions are met, your app receives a delivery like this:
{
"topic": "Product",
"action": "update",
"handle": "price_sync",
"data": {
"productVariant": {
"id": "gid://shopify/ProductVariant/456",
"price": "24.99",
"compareAtPrice": "34.99",
"sku": "SIGNAL-NOT-NOISE"
},
"product": {
"id": "gid://shopify/Product/123",
"title": "Peace & Quiet Tee",
"status": "ACTIVE"
}
},
"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"
}
}
The delivery includes the data returned by your query, the paths that explain why the subscription fired, and the Shopify used to run the GraphQL Admin API query. Your app no longer needs to infer whether price changed by comparing the payload against stored state, discard unrelated product updates that happened to pass a current-state filter, or make a second request just to collect the data needed for the price sync.
Anchor to Get startedGet started
Events is in developer preview and uses the unstable API version. To try Events:
- Upgrade to Shopify CLI version 3.92 or higher.
- Add an
[events]block toshopify.app.toml. - Create a subscription for a supported topic from the Events reference.
- Add
triggersfor the field changes that matter to your workflow. - Add a GraphQL
queryand, if needed, a. - Deploy the app configuration and test deliveries in a development store.
Start with the Create an Events subscription tutorial for a working product sync example, or move right into migrating your existing subscriptions into Events.
Anchor to What's nextWhat's next
Events are available in developer preview while topic coverage expands. For topics that aren't supported yet, keep using webhooks alongside Events in the same shopify.app.toml.
As you test, look for workflows where your app currently receives broad webhook deliveries, compares them against stored state, filters them in your handler, and then calls the GraphQL Admin API to fetch more data. Those workflows are strong candidates for Events subscriptions.
If you want more background on the developer preview, follow the launch thread, watch the Events walkthrough, or join the Developer Community discussion.