Weaverse LogoWeaverse
All Articles
Paul Phan
12 mins read

Shopify Next Gen Events Is GA: 2026-10 Migration Guide

Shopify Next Gen Events is GA on 2026-10. Update preview subscriptions, keep classic webhooks where needed, and validate triggers, payloads and queries.
#shopify#hydrogen#webhooks#events#app-development
Shopify Next Gen Events Is GA: 2026-10 Migration Guide
Table of Contents

Update, October 1, 2026: Shopify made Next Gen Events generally available on Events API version 2026-10 across 18 topics. This supersedes our development-only recommendation below: supported Events workflows can now run in production. Existing classic webhooks continue to work; migrate one workflow at a time, and keep webhooks for topics or patterns Events does not yet support.[10][11]

GA coverage: Product and Collection; Customer and Company; Order, FulfillmentOrder, Refund and Return; InventoryItem, InventoryShipment, InventoryTransfer and Location; Article, Blog and Page; MetafieldDefinition, Metaobject and MetaobjectDefinition. Check each topic's supported triggers, query variables and access scopes in the versioned reference. GA does not imply a one-for-one replacement for every classic webhook.[10][11]

Preview users: Change [events].api_version from unstable to 2026-10, including any subscription-level overrides. Review the topic reference, test the receiver, and deploy the updated app configuration. Preserve the existing webhook path until the Events subscription and your handler cover the fields, lifecycle changes and recovery your app needs.[10][11][12]

Start with a webhook workflow that discards many deliveries or fetches more data after most deliveries. A low-volume handler that already works need not migrate merely because Events is GA. The September payload, trigger and 100-point query rules below still apply; keep signature verification, deduplication and reconciliation.[10][12]

CLI version discrepancy: Shopify's GA announcement says Shopify CLI 4.83 or later for new Events setups, while its 2026-10 Events reference says 3.92 or later. Both are current official pages. Verify your installed CLI against the configuration you plan to deploy rather than assuming either minimum guarantees this workflow.[10][11]

Update, September 22, 2026: Shopify lowered the Events subscription query complexity limit from 250 to 100 points and marked the change Breaking API Change / Action required. The limit applies to each Events subscription query; Classic Webhooks are unaffected.[4]

Shopify calculates complexity from selected GraphQL fields, the types returned, and connection sizes set with first or last. Subscription queries do not count against an app's API rate limits.[4] The 100-point cap is not a 100-field cap: Shopify's example with product details, SEO, options, metafields and up to 250 variants scores 100 points. That example does not guarantee another query will fit, or that the first page contains the variant that changed.[6][7]

Shape each query around the change it handles. Query the affected variant directly when its trigger provides $variantsId, rather than fetching a page of unchanged siblings; use a separate subscription for product-level changes that only provide $productId. Keep the fields and relationship IDs your handler needs.[4][7] If a necessary query still needs more than 100 points, Shopify is open to discussing an exception case by case in its developer forum.[6]

Update, September 17, 2026: Shopify changed the Events preview contract on September 16. fields_changed is now an object with added, updated and removed arrays; parent triggers need an explicit .*; subscriptions with the update action require a trigger; and two delivery headers have been removed. Classic Webhook subscriptions are unaffected.[1]

That development-only recommendation was superseded by the October 1 GA release above. For production use, validate the 2026-10 topic, triggers and receiver for each workflow; retain classic webhooks where coverage or recovery is incomplete.[10][11]

If you copied the original examples in this article, update your payload parser first. Check parent-trigger syntax before your next app-configuration deploy. These are different migration boundaries: an existing subscription continuing to work does not mean its delivered payload keeps the old shape.[1]

What changed on September 16

1. fields_changed describes how a path changed

The old flat array identified a path, but could not distinguish an addition from a removal. The new object separates those changes:[1]

  • added: A resource or relationship was added.
  • updated: A value changed on an existing resource.
  • removed: A resource or relationship was removed.

Here is Shopify's variant-addition example, shown as payload fragments rather than complete deliveries.[1]

Before September 16, historical format only:

{
"topic": "Product",
"action": "update",
"fields_changed": [
"product[id: 'gid://shopify/Product/123'].variants[id: 'gid://shopify/ProductVariant/456']"
]
}

Current format for the same variant addition:

{
"topic": "Product",
"action": "update",
"fields_changed": {
"added": [
"product[id: 'gid://shopify/Product/123'].variants[id: 'gid://shopify/ProductVariant/456']"
],
"updated": [],
"removed": []
}
}

Notice that adding a variant is still an update on the Product, not a Product create. The action describes the root entity's lifecycle; the buckets describe what happened inside it. A price change goes in updated, while removing a variant from a surviving product puts its path in removed.[1][8]

Keep those distinctions in your processing code. Flattening all three arrays into one list discards the information this change adds.

2. Parent triggers need a terminal wildcard

A parent path that subscribes to all supported descendant fields must now end in .*. These are configuration fragments, not complete subscriptions.[1]

Old parent-trigger syntax, replace before redeploying:

triggers = [
"product.variants",
"product.options.optionValues.swatch"
]

Current parent-trigger syntax:

triggers = [
"product.variants.*",
"product.options.optionValues.swatch.*"
]

Leaf triggers do not change. product.variants.price still means variant price changes; do not append .* to it. The wildcard migration preserves matching behavior and delivery volume for equivalent subscriptions. It makes an existing subscription's breadth explicit, rather than making that subscription narrower.[1]

Shopify said existing subscriptions would continue to work through that September syntax change, but parent triggers needed the new form on the next shopify.app.toml deploy. Review them again as part of the 2026-10 cutover.[1][11]

3. An update subscription must include a trigger

Subscriptions whose actions include update now require at least one trigger. Do not use an omitted or empty triggers list as your new configuration for receiving all updates. Shopify says existing subscriptions continue to work.[1]

Older tutorials may still show optional update triggers or a flat-array payload. The September changelog and versioned 2026-10 reference require triggers for update and show the object-shaped fields_changed contract. Use the versioned reference for a new deployment.[1][11]

4. Stop requiring the two removed headers

Events deliveries no longer include shopify-event-id or shopify-resource-id. Shopify directs developers to update their Shopify API packages to versions that handle the removal. Custom code must also stop reading or requiring those headers for validation.[1]

Do not remove authentication or duplicate-delivery handling. For HTTPS Events, verify Shopify-Hmac-Sha256 against the raw body before trusting it. Shopify-Webhook-Id remains the delivery identifier used for deduplication; header names are case-insensitive.[9]

Use the delivery's query_variables for the entity IDs available to that subscription, rather than assuming the removed resource-ID header is present.[5]

A current price-sync subscription

Events moves three decisions into the subscription: which field change qualifies, what data Shopify queries, and whether the query result satisfies a delivery filter. Classic webhooks already support payload trimming and state-based filters; Events adds explicit change triggers and a custom GraphQL payload.[2]

The price-sync example keeps its leaf triggers, which are supported in the 2026-10 Product reference. It combines a trigger, query and filter in one shopify.app.toml example; only the Events API version changes here.[11][13]

Merge these settings into your existing app configuration. Preserve other access scopes and subscriptions, and implement the receiver at the example uri. This is subscription configuration, not a complete app.[3]

[access_scopes]
scopes = "read_products"
[events]
api_version = "2026-10"
[[events.subscription]]
handle = "price_sync"
topic = "Product"
actions = ["update"]
triggers = [
"product.variants.price",
"product.variants.compareAtPrice"
]
uri = "/events/app/products"
query = """
query priceSync($productId: ID!, $variantsId: ID!) {
productVariant(id: $variantsId) {
id
price
compareAtPrice
}
product(id: $productId) {
id
title
status
}
}
"""
query_filter = "product.status:'ACTIVE'"

For this subscription:

  • A qualifying variant price or compare-at-price change can trigger delivery. An unrelated title edit alone does not qualify.[11][13]
  • Shopify runs the GraphQL query and places its response in data. The variant-level triggers make both $variantsId and $productId available.[5]
  • query_filter checks the query result and suppresses delivery unless product.status is ACTIVE. Fields used by the filter must be returned by the query.[11][5]

The query runs after the qualifying change. Its result is not an immutable snapshot of the instant that change occurred. Handle GraphQL errors and missing resources, and keep reconciliation for your external state; custom payloads do not make every consistency problem disappear.[5]

The same applies to filtering. A price-only subscription filtered to active products is not a complete catalog-lifecycle sync: it is not your path for removing a product from an external index when its status changes to inactive. Design separate subscriptions or reconciliation for the lifecycle changes your application needs.

Update the parser without hiding malformed deliveries

The following small helper illustrates only the new fields_changed shape. Call it after authenticating the delivery and parsing its JSON; it does not replace signature verification, full-envelope validation or delivery deduplication.[9]

export function readFieldsChanged(payload) {
const changes = payload?.fields_changed;
if (!changes || typeof changes !== "object" || Array.isArray(changes)) {
throw new TypeError("Expected object-shaped fields_changed");
}
for (const kind of ["added", "updated", "removed"]) {
if (
!Array.isArray(changes[kind]) ||
!changes[kind].every((path) => typeof path === "string")
) {
throw new TypeError(`Expected fields_changed.${kind} to be a string array`);
}
}
return changes;
}

Route each returned bucket to its corresponding application logic. Do not default malformed or missing buckets to empty arrays and then acknowledge the delivery as successfully processed. Surface the validation failure through your receiver's error-handling path.

If you replay archived deliveries, keep their legacy format explicit. An old flat array does not tell you whether a path was added, updated or removed, so blindly assigning every old path to updated invents information. The helper above intentionally rejects that old format.

What this means for Hydrogen and Weaverse storefronts

These are app-side subscriptions in shopify.app.toml, not settings you add to a Hydrogen browser client. A backend supporting a Hydrogen storefront can use Events to drive targeted work, such as refreshing an external search record or invalidating an application-owned product cache.[2]

The migration task is at that receiver and its downstream processing:

  • Cache invalidation: Preserve the affected path and entity IDs, including additions and removals. Changing the payload contract does not automatically invalidate an Oxygen or Hydrogen cache.
  • Search synchronization: Distinguish a variant being removed from a variant price changing. Test the separate product-deletion path too.
  • Query consumers: Account for errors, null resources and state changing between the event and query execution, rather than assuming every delivery contains a complete resource snapshot.[5]

For a Weaverse storefront, visual content editing and Shopify event processing remain separate concerns. Keep the event receiver's authentication, idempotency and failure recovery in the backend design. Do not replace that design with an unauthenticated storefront endpoint or an assumption that copying a subscription handles the rest.

Migration checklist

  1. Inventory both paths. Identify existing Events subscriptions and the classic webhook workflows you might migrate, including the fields, filters and recovery each handler needs. The September payload and header changes applied to Events, not classic webhooks.[1][12]
  2. Update payload handling now. Read added, updated and removed, and test each separately. Updating Shopify API packages for the header change does not rewrite your custom payload parser.[1]
  3. Review every update subscription. Provide at least one valid trigger. Add .* to parent paths before the next configuration deploy; leave leaf triggers unchanged.[1]
  4. Audit each Events subscription query. Inventory the fields your handler and query_filter use, estimate complexity for the pinned Events API version, and validate the configuration with shopify app deploy --no-release before releasing. Shopify checks the 100-point limit without executing the query and reports the calculated cost if validation fails. The command creates an unreleased app version; it does not release subscriptions to installed stores.[4][7]
  5. Reshape queries without losing coverage. Split subscriptions by the work and trigger variables they need. For variant-price changes, query the affected variant instead of the Product's first page of variants; a product-title trigger does not provide $variantsId. Preserve required fields, join IDs and reconciliation for truncated collections, and compare coverage with the old query before switching.[4][7]
  6. Review deletion assumptions. Shopify's linked migration post describes removing child-cascade update Events after a root resource is deleted. Test root deletion separately from removing one variant from a surviving Product; do not rely on follow-up child updates as your only cleanup mechanism.[8]
  7. Exercise the receiver in a development store. Test addition, value change and removal; a non-matching title edit; a matching price change; duplicate delivery; invalid signature; and a GraphQL error or missing resource. Keep a test for a delivery that lacks the two removed headers but has the required authentication and delivery-ID information.[1][11][9]
  8. Cut over one workflow at a time. Move preview subscriptions and per-subscription version overrides from unstable to 2026-10. Recheck topic triggers, variables, scopes and query coverage, validate an unreleased app version, and inspect actual deliveries before relying on them. Keep classic webhooks in parallel until the new handler and recovery path are proven; retain them for unsupported patterns.[10][11][12]

The bottom line

Next Gen Events is now a production option on 2026-10, not a mandate to replace every webhook. Choose workflows where targeted triggers and GraphQL payloads remove unnecessary work; keep classic webhooks where coverage or a proven integration makes them the safer choice.[10][12]

GA does not erase the September contract changes. Keep each subscription query within its 100-point budget, preserve the fields_changed categories, migrate parent triggers without altering leaf triggers, and remove obsolete header dependencies without weakening verification. Measure delivery reduction in your own workload instead of assuming a universal savings figure.[1][7]

If you're planning the event layer behind a Hydrogen storefront, talk to us about the integration. Start with the state your storefront must keep correct, then choose subscriptions and recovery behavior around it.

Sources

  1. Shopify: Updates to Events payloads and subscription configuration
  2. Shopify: Events and webhooks
  3. Shopify: Manage Events subscriptions
  4. Shopify: Events subscription query limit changes to 100 points
  5. Shopify: Events delivery structure
  6. Shopify Developer Community: Events query complexity limit
  7. Shopify: Optimizing your subscriptions
  8. Shopify Developer Community: Upcoming changes to Events
  9. Shopify: Verify Events deliveries
  10. Shopify: Next Gen Events are generally available
  11. Shopify: Events 2026-10 API reference
  12. Shopify: Migrate from webhooks
  13. Shopify: Product Events reference, 2026-10

Reactions

Like
Love
Celebrate
Insightful
Cool!
Thinking

Join the Discussion

Never miss an update

Subscribe to get the latest insights, tutorials, and best practices for building high-performance headless stores delivered to your inbox.

Join the community of developers building with Weaverse.