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 — 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 use case — "notify my app when a product's price changes" — 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, 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. This is worth internalizing — actions tells you what happened to the entity; triggers and fields_changed tell you what changed inside it.
triggers — The headline feature. 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 — 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. 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, 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"
}
}
No more diffing against prior state. No more "what changed" follow-up GraphQL queries. The payload tells you.
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 — same shape as Bulk Operations. Your endpoint needs to handle both forms.
What's available today
- Topics:
ProductandCustomeronly. - API version:
unstable— 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 — meaning 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.
Shopify has committed to bringing every topic available in classic webhooks across to Events through 2026. No use case will be left behind.
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 — products/update deliveries dropped because they were inventory or tag changes you didn't care about, customers/update deliveries triggered by fields outside your app's concern.
Rank by cost and noise, not by topic order
The point of Next Generation Events is to stop paying — in compute, queue depth, downstream API calls, and BFCM scale anxiety — for deliveries you discard. Sort your audit by "noise ratio" (deliveries dropped vs. deliveries acted on). Anything north of 50% is a strong migration candidate as soon as the topic is covered.
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 — it's a composite of the underlying event and your subscription, unique per 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 carries over.
Plan for the download-envelope shape
If your query could return significant data — deep variant lists, large metafields, rich HTML — your handler needs to handle both the inline payload and the payload_url envelope from the start. Don't bolt this on later.
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 — 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. CLI users activate faster and ship migrations faster — it's worth installing before you write your first TOML subscription. - 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 — no credit card, and the CLI works without an account if you just want to experiment.
What hasn't changed
Worth naming explicitly, because it affects how you plan:
- 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 behaviour 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:
- 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. File references will land before stable.- Topic expansion.
ProductandCustomerare the only topics in Preview.Order,Inventory, and the rest are expected through 2026. - Stronger delivery and replay primitives. Shopify has signalled that replayability and stronger delivery guarantees are being looked at, though no commitments yet.
A clever workaround for the staged-rollout problem in the meantime: filter by shop ID in query_filter, or include it in your query and gate downstream in your handler. Not perfect, but it lets you dark-launch a subscription on a subset of merchants today.
Wrapping up
Next Generation Events are a meaningful upgrade — not just 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. The "what changed" question is answered in the payload. And the long-standing translation layer between webhook-shaped data and API-shaped data goes away.
The work to migrate isn't trivial — TOML configs, GraphQL queries, dual-running during transition — 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 — [get started here]/event-gateway). And if you want to try receiving events on your laptop before you've even written the TOML, the Hookdeck CLI is the fastest way to do it.
For more on Shopify webhooks generally, our getting started guide, Admin API tutorial, and features and best practices guide are the rest of the set.