App development

Shopify Events vs webhooks: field-level triggers and custom GraphQL payloads

Shopify's next-generation Events replace fixed-payload webhook topics with subscriptions that declare which fields must change and what GraphQL shape to return. Here's how they differ, and when to migrate.

Bas Lefeber

Founder, learnshopify.dev · July 30, 2026 · 6 min read

Ready to learn Shopify development?Short, interactive lessons where you write real Liquid against a live storefront and watch it change as you type. Free, and genuinely fun.

Every Shopify app that reacts to store data eventually writes the same unhappy function. You subscribe to products/update, you receive the entire product on every change, and because the payload does not tell you what changed, you diff it against a copy you stored last time to work out whether the one field you actually care about moved. On a catalog of any size, the overwhelming majority of those deliveries are noise you paid to receive, parse, store, and discard.

Shopify's next-generation Events are built to delete that function. On July 21, 2026, at DotDev, they gained metafield triggers and a batch of new topics, which is the point at which they start covering real production use cases. This post is about what actually changes in your app, and how to decide when to move.

Status check before you plan a migration

Events is in developer preview on the unstable API version and covers a subset of topics. Webhooks remain the correct choice for production today and are not deprecated. Use Events to prototype and to shape your roadmap, not to carry a customer's order pipeline this quarter.

Webhooks filter in your handler, after you have already paid to receive the delivery. Events filter at the source.

The core difference in one sentence

A webhook subscription says "tell me when a product changes" and gets a fixed payload. An Event subscription says "tell me when this specific field on a product changes, and when you do, run this GraphQL query and send me the result".

That moves three responsibilities from your handler to the platform: deciding whether a delivery is relevant, deciding what data to include, and telling you which field moved.

AspectWebhooksEvents
Configured inshopify.app.toml or the GraphQL Admin APIshopify.app.toml only
Topic formattopics = ["products/update"]topic = "Product" + actions = ["update"]
Field filteringinclude_fields trims the payload after the facttriggers decide whether you get a delivery at all
Payload shapeFixedA GraphQL query you write
What changed?Work it out yourselffields_changed array, with paths
CoverageAll Shopify resourcesDeveloper preview subset
Events and webhooks side by side, per Shopify's own comparison.

The row that matters most is the third one. include_fields on a webhook makes the payload smaller; it does not stop the delivery. triggers on an Event is pre-qualification at the source: if the field did not change, nothing is sent, and your endpoint never wakes up.

What a subscription looks like

Events are declared in shopify.app.toml, and only there. A price-tracking subscription reads roughly like this:

shopify.app.toml
[events]api_version = "unstable" [[events.subscription]]handle = "price_sync"topic = "Product"actions = ["update"]triggers = ["product.variants.price", "product.variants.compareAtPrice"]uri = "/api/events/price_tracker" 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'"

Each field is doing a specific job:

  • handle is a unique identifier scoped to your app, which means you can have several subscriptions on the same topic for different purposes instead of one overloaded endpoint that switches on payload contents.
  • topic maps to a GraphQL type from the Admin API (Product, Customer), not to a slash-separated string. actions are standardised to create, update, and delete.
  • triggers is the field-level pre-qualification, written in dot notation. Omit it and you get every change to the topic, which is webhook behaviour.
  • query is standard GraphQL defining the payload you want back, subject to complexity limits. This is where you stop receiving 40KB of product you did not ask for.
  • query_filter runs after the query and suppresses the delivery conditionally. Fields referenced here have to appear in the query, which is why product { status } is selected above even though the app mostly cares about price.

Read the two filter stages as a pipeline

There are two chances to drop a delivery: triggers (before anything is queried, based on which fields moved) and query_filter (after the query, based on the resulting values). Push work into triggers wherever you can, since it is the cheaper of the two and it never touches your endpoint.

The payload tells you what changed

This is the part that removes stored state from your app. The delivery carries your query result, the variables that were resolved, and an explicit list of the fields that triggered it:

json
{  "data": {    "productVariant": { "id": "gid://shopify/ProductVariant/456", "price": "24.99" },    "product": { "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"  }}

Look at fields_changed. It is not a field name, it is a path with IDs resolved along it, so on a nested change you know which variant of which product moved without walking the payload to find out. The shadow copy of every product you were keeping purely to compute diffs stops earning its keep.

What July 21 actually added

Two things, and the second is the reason to pay attention now.

More topics. Apps can now subscribe to Order, Collection, InventoryItem, InventoryShipment, and Location, alongside the existing Product, Customer, and ProductVariant coverage. Inventory and location coverage in particular is what makes Events viable for the fulfilment and ERP-sync category of app.

Metafield change triggers. You can now trigger on changes to specific metafields, by namespace and key, across Product, Order, Customer, Collection, and Location. ProductVariant metafields come through the Product topic. Both $app metafields and regular metafields work, and normal access control applies: your app has to have access to a metafield to subscribe to it or receive it in a query.

Why metafield triggers are the interesting one

Metafields are where apps put their own data. Before this, an app that needed to react to its own custom field had to subscribe to every update of the parent resource and diff, which is the single most common source of wasted webhook volume in the ecosystem. Subscribing to two targeted metafields instead of all product updates is not an optimisation, it is a different order of magnitude.

Each filter stage runs before your endpoint does. The work you delete is the work you never receive.

How to decide what to do

Events is preview software, so the honest recommendation is not "migrate." It is "find out what you would migrate, and stop building things that Events will make obsolete." Three concrete moves:

  1. Audit your subscriptions for diff-only topics. For every webhook topic your app subscribes to, ask what fraction of deliveries lead to an actual action. Any topic where the answer is under a few percent is a subscription that exists only to detect a change, and it is a direct Events candidate.
  2. Stop building payload-diffing infrastructure. If you are about to add a stored-snapshot table so you can compute what changed, that table has a defined shelf life now. Weigh a simpler interim approach against it.
  3. Prototype one subscription on the unstable version. Pick the noisiest topic you have, write the equivalent Event subscription, and measure the delivery volume difference on a development store. That number is your migration business case, and you will want it written down before you argue for the engineering time.

Keep both mechanisms in mind for the long run, too. Webhooks still cover every Shopify resource and support flexible delivery destinations; Events cover a preview subset with far more precision. They coexist in the same shopify.app.toml, and Shopify's own docs frame them as complementary rather than as a straight replacement. The realistic end state for most apps is Events for the high-volume topics where precision pays, and webhooks for the long tail.

Learn this properly · free lesson

App vs theme vs Function: which tool for which job

Choosing where logic lives, an app reacting to events, a theme, or a Function, is the decision that shapes everything after it. Work through the real tradeoffs on a concrete build. Free lesson, no signup.

Try this lesson — free

The through-line worth taking from this release is not the TOML syntax, which will change. It is that Shopify is moving app integration from "receive everything, sort it out downstream" to "declare precisely what you need." Declarative subscriptions are cheaper to run, easier to reason about, and considerably easier for an AI agent to generate correctly, because the intent is in the configuration instead of buried in a handler. That pattern is going to keep showing up.

Frequently asked questions

What is the difference between Shopify Events and webhooks?

A webhook subscribes to a topic string like products/update and delivers a fixed payload on every change, leaving your app to work out what changed. An Event subscription declares a GraphQL topic and action, field-level triggers that decide whether a delivery happens at all, and a custom GraphQL query defining the payload shape. The delivery also includes a fields_changed array, so you do not need to diff payloads to detect what moved.

Do Shopify Events replace webhooks?

Not yet, and not entirely. Events is in developer preview on the unstable API version and covers a subset of topics, while webhooks cover every Shopify resource and support flexible delivery destinations. Both are configured in the same shopify.app.toml and can coexist. Webhooks remain the right choice for production today.

Can Shopify Events trigger on metafield changes?

Yes, as of July 21, 2026. You can add targeted metafield triggers for a specific namespace and key on Product, Order, Customer, Collection, and Location topics, with ProductVariant metafields delivered through the Product topic. Both $app metafields and regular metafields are supported, and your app must have access to a metafield to subscribe to it or query it.

Which topics do Shopify Events support?

The July 2026 update added Order, Collection, InventoryItem, InventoryShipment, and Location to the existing Product, ProductVariant, and Customer coverage. It remains a developer-preview subset rather than full parity with webhook topics, so check the current documentation before planning around a specific resource.

How do you configure a Shopify Event subscription?

In shopify.app.toml only, unlike webhooks which can also be created through the GraphQL Admin API. An [[events.subscription]] block sets a unique handle, a topic matching an Admin API GraphQL type, actions from create/update/delete, optional dot-notation triggers for field-level filtering, a delivery uri, an optional GraphQL query defining the payload, and an optional query_filter that suppresses deliveries based on the query results.

Start free

Ready to become a Shopify developer?

You just read how it works. Now write it yourself: real tickets from a live store, in an editor where the storefront updates as you type. Module 1 is free, no card.

Start your first lesson

Free · No credit card · Your first win in minutes

app-developmentPlatformAdmin APIwebhooks

About the author

Bas Lefeber, Founder, learnshopify.dev

Bas builds learnshopify.dev, where developers learn production-grade Shopify theme development against a live storefront. He writes about Liquid, theme architecture, and the parts of the job that still matter now that AI writes the code.

Keep going in the curriculum