Shopify Next Generation Events: What's Changing and How to Migrate
Shopify's Next Generation Events is the biggest change to Shopify's webhook architecture in a decade. Topic subscriptions with fixed payloads are being replaced by filterable, GraphQL-defined event subscriptions configured in your app's TOML.
If you've used Shopift webhooks before, the mental model you've been working with is about to change. This guide walks through what's changing, what stays the same, and how to plan a migration without breaking the apps you've already shipped.
Next Generation Events are in Developer Preview on the
unstableAPI version. Coverage is limited to theProductandCustomertopics. Shopify recommends against using them in production until a stable release. This guide is for planning and early experimentation.
The mental shift
Today, a Shopify webhook subscription looks roughly like this:
- Pick a topic, e.g.
products/update. - Receive the full product payload every time anything on that product changes, like title, price, tags, a single variant SKU, inventory.
- Diff the payload against your own state to figure out what actually changed, or fire side effects regardless.
This is the source of the well-known cost and noise problem. Most products/update deliveries are inventory or field updates an app doesn't care about, and there's no way to filter them at the source.
Next Generation Events restructures the subscription itself. Each subscription now declares:
- What entity you're subscribing to (the topic, e.g.
Product). - What lifecycle action you care about (
create,update,delete). - What field-level changes should trigger delivery (the triggers).
- What payload shape you want back (a custom GraphQL query against the Admin API).
- What conditions must be true of the returned data for the event to be delivered (a query filter).
Filtering happens before delivery, not after. The payload is whatever GraphQL shape you asked for and the payload tells you which fields actually changed.
Old setup vs Next Generation Events, side by side
Here's the same "notify my app when a product's price changes" use case under both models.
Today (Admin API webhook subscription):
curl -X POST "https://{shop}.myshopify.com/admin/api/2026-01/webhooks.json" \
-H "X-Shopify-Access-Token: {token}" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"topic": "products/update",
"address": "https://your-app.com/webhooks/products-update",
"format": "json"
}
}'
Your endpoint then receives the full product payload on every product update — title changes, tag edits, variant tweaks, inventory blips. You diff against stored state to detect the price change and then act.
Next Generation Events (TOML subscription):
[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'"
Your endpoint receives only the events that match (a variant price change on an active product) with exactly the data you queried for, plus a fields_changed array telling you which fields fired the event.
Anatomy of a subscription
The TOML block is doing more work than the old webhook spec, so it's worth understanding each piece.
handle — A unique identifier for the subscription, scoped to your app. You can have many subscriptions on the same topic, each handling a different concern (price sync, SEO sync, status changes). The handle comes through in every payload so your app knows which subscription is firing.
topic — Maps directly to a GraphQL type from the Admin API (Product, Customer). This is one of the biggest wins of Next Generation Events: the schema universe of your webhooks and your API queries are now the same.
actions — Standardized to create, update, and delete. These describe the lifecycle of the topic entity itself, not changes inside it. Adding a variant to a product is update on Product, not create. So actions tells you what happened to the entity; triggers and fields_changed tell you what changed inside it.
triggers — Field-level pre-qualification at the source. The example above only fires when product.variants.price or product.variants.compareAtPrice change. Title edits, tag changes, and inventory updates don't deliver. Triggers are optional, so leave them off to subscribe to every change on the topic.
query — A standard Admin GraphQL query that defines exactly what payload your app receives. There's no fixed schema. You can query across entities, pull in metafields, or stitch in shop data — anything you have scope for, up to a complexity limit of 250 points during Preview. Query is also optional: leave it off and you get a thin envelope with just fields_changed and query_variables, which can be useful if you want to fan out to handlers before deciding what to fetch.
query_filter — Filters on the data returned by your query. The example only delivers when the product is ACTIVE. Any field referenced in the filter must be selected in the query.
query_variables — Not declared explicitly. When a subscription fires, IDs flow up from the changed entity through the GraphQL hierarchy. A variant price change makes both $variantsId and $productId available to your query. Variable names follow GraphQL field names on the parent. Product has a variants field (plural), so the variable is $variantsId, not $variantId.
What you actually get in the payload
Every Next Gen Event delivery includes fields_changed, telling you exactly which fields fired the event, with full entity paths:
{
"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"
}
}
This means you no longer have to diff against prior state or send "what changed" follow-up GraphQL queries. It's all there in the payload.
If your query pulls a lot of data and exceeds the size limit for your delivery method, Shopify swaps the inline payload for a small envelope with a payload_url pointing to the full payload.
What's available today
- Topics:
ProductandCustomeronly. - API version:
unstable, so APIs may change before the stable release. - Delivery methods: HTTP endpoints, Google Cloud Pub/Sub, and Amazon EventBridge.
- Subscription management: TOML only. No GraphQL mutations to manage subscriptions yet so a config change deploys to all merchants at once. GraphQL-based subscription management is on the roadmap.
- Query files: Queries must be inlined in TOML for now.
.graphqlfile references are coming. - Query complexity: Capped at 250 points during Developer Preview.
A migration playbook
You don't need to move everything on day one. The right pattern is incremental: pick the topics where you'll see the biggest impact, prove the migration on those, then expand as Shopify adds coverage.
Audit your current subscriptions
Run an inventory of every webhook topic your app subscribes to today, what your handler actually does with each, and how much volume you're absorbing per topic. The migrations that pay off first are the high-volume, low-relevance ones.
Historically product/update webhooks have been the most noisy and should be a prime suspects for migration if you already subscribe to it.
Translate each subscription to a Next Gen Events block
For each subscription, decide:
- Triggers — what specific fields, when changed, should fire your handler? Be as narrow as the trigger surface allows. If the field you need isn't yet triggerable, note it for feedback to Shopify.
- Query — what does your handler actually need? Resist the temptation to fetch the full entity out of habit; Next Generation Events lets you ask for exactly what you'll use.
- Query filter — what's the gate that disqualifies this event entirely? An inactive product, a customer in a state your app doesn't handle, an order from a market you don't operate in.
- Handle — meaningful and unique. You'll likely have multiple subscriptions per topic.
Run old and new in parallel
Classic webhooks aren't going away. Shopify has been clear that every existing topic will eventually have a Next Gen Events equivalent, but no deprecation date is on the table. The safe pattern: add the Next Generation Events subscription alongside the existing webhook subscription, send both to your app, and feature-flag which one your handler trusts. Once the new path has run cleanly through enough volume, retire the old subscription.
Update your idempotency and verification
Idempotency keys change. Use Shopify-Webhook-Id from the Next Gen Events delivery. Shopify-Event-Id identifies the underlying change and may appear across multiple subscriptions when a single change fires more than one.
HMAC verification still applies. The same secret and X-Shopify-Hmac-Sha256 flow described in our API tutorial still applies.
Plan for the download-envelope shape
If your query could return significant data (like deep variant lists, large metafields, rich HTML, etc.) then your handler needs to handle both the inline payload and the payload_url envelope from the start.
Receiving Next Generation Events with Hookdeck
The TOML lives in your app config. The delivery still hits an HTTPS endpoint, a Google Pub/Sub topic, or an Amazon EventBridge bus. Everything Hookdeck does for classic Shopify webhooks (observability, filtering, routing, retries, replay, local development) works for Next Generation Events too.
A few places where this is particularly useful during migration:
- Run old and new side by side without changing your app. Point both subscriptions at Hookdeck and use filters and routing rules to send them to different handlers, different environments, or different teams.
- Inspect the new payloads. The Hookdeck dashboard gives you a structured view of every delivery, the
fields_changedarray, and the rendered GraphQL payload, which is useful when iterating on triggers and queries. - Test in local development. Run Hookdeck CLI and use
hookdeck listento forward Shopify events to your local server. - Replay events when you change your query. Iterating on a GraphQL query for a Next Gen Events subscription is the kind of thing that benefits from being able to replay the same trigger against new handler logic. Hookdeck stores deliveries so you can replay them without re-triggering the underlying event in Shopify.
Try it free and the CLI works without an account if you just want to experiment.
What hasn't changed
- Delivery is still at-least-once. Next Generation Events tighten what gets delivered, but they don't change delivery guarantees. Your handler still needs to be idempotent. Reconciliation jobs that periodically fetch state from the Admin API are still the right pattern for catching anything missed.
- The 5-second timeout still applies on HTTP deliveries. If your handler does heavy work, queue it and acknowledge fast (exactly the same pattern as today).
- Retry behavior is unchanged. Failures are retried with exponential backoff, and subscriptions defined in
shopify.app.tomlare not auto-deleted on persistent failure (unlike subscriptions created via the Admin API). - Mandatory compliance webhooks still live in their own world.
customers/data_request,customers/redact, andshop/redactare configured undercompliance_topicsin your app TOML and aren't part of Next Generation Events.
What to wait for before going all-in
A few capabilities are explicitly on the roadmap that may affect how aggressively you migrate:
- General availability. Events subscriptions are in Developer Preview on the
unstableAPI version. Coverage is limited to theProductandCustomertopics. Shopify recommends against using them in production until a stable release. - GraphQL-based subscription management. Today, subscription changes deploy to all merchants at once via TOML. The planned GraphQL mutation API will let you stage rollouts per merchant.
.graphqlfile references. Inlined queries in TOML are workable but harder to type-check and reuse.- Topic expansion.
ProductandCustomerare the only topics in Preview.Order,Inventory, and the rest are expected through 2026.
Wrapping up
Next Generation Events are a meaningful upgrade. Not just only on cost and noise, but on how Shopify webhook integrations are structured. The schema universe collapses into the same GraphQL surface you already use for the Admin API and the "what changed" question is answered in the payload.
The work to migrate isn't trivial, but the payoff is real, especially for apps drowning in products/update and customers/update noise today.
If you're planning a migration and want to test the dual-running pattern, run new and old subscriptions through Hookdeck and route them however makes sense.
For more on Shopify webhooks generally, check out our getting started guide, Admin API tutorial, and features and best practices guide.