Shopify Functions & APIs

Migrate Shopify Webhooks to Events: Triggers, Queries and Limits

By CartStack Team6 min read

To migrate Shopify webhooks to Events, start from the fact that with API version 2026-10 Events are generally available as the successor to classic webhooks. Migrating is not a find-and-replace, because the work moves from your handler into the subscription. This guide follows Shopify’s migration steps: translate the fields you read, choose what should trigger a delivery, and optimize the query to fit the 100-point limit. It also lists what Shopify says to keep on webhooks for now. Checked against Shopify’s migration guide and its October 1, 2026 announcement.

Cover graphic for migrating Shopify webhooks to Events
Design the subscription around the change, not around the old payload.

Before You Migrate Shopify Webhooks to Events

Events use the same delivery infrastructure as classic webhooks, with field-level triggers and data chosen by a GraphQL query. Your existing webhooks keep working, and you can run both in one app and move workflows over one at a time. Shopify’s migration guide lists Shopify CLI 3.92 or higher, while the general availability announcement says 4.83 or later for new apps. The two numbers disagree, so run shopify version, follow the upgrade instructions, and prefer the newer one. If you already use the developer preview, change the events API version from unstable to 2026-10, including any subscription-level overrides. For the concepts, read Shopify Events vs Webhooks first.

Pick the first workflow carefully. Shopify suggests starting where your app receives many updates, discards most of them, or makes another API call on every delivery.

Step 1: Translate the Webhook Fields You Read

Shopify’s guide maps each classic topic to an Events topic and gives a starter GraphQL query. REST field names generally change from snake_case to camelCase, but some fields change shape or have no equivalent. These are the differences that matter most.

  • admin_graphql_api_id becomes id. Numeric REST IDs have no Events equivalent.
  • Flat arrays such as variants, images and line items become connections with nodes.
  • Orders: financial_status becomes displayFinancialStatus, fulfillment_status becomes displayFulfillmentStatus, and prices come back as money sets.
  • Products: body_html becomes bodyHtml, tags return as an array instead of a comma-separated string, and option1 to option3 become selectedOptions.
  • Enums are uppercase, for example product status ACTIVE, DRAFT or ARCHIVED.

Some fields are simply gone. Customers have no currency, and variants have no old_inventory_quantity. Audit your handler for anything that depends on these before you change a subscription. Shopify stresses that the starter queries only help with field mapping. They do not configure a subscription or reproduce the conditions that fired the classic webhook.

Step 2: Decide What Triggers the Events Delivery

A query selects data. It does not decide when the subscription fires. For each webhook you migrate, identify the change your app reacts to, then find the matching topic, action and triggers in the Events reference.

  • Create: query the new resource by its ID.
  • Update: every subscription whose actions include update requires triggers. Choose the specific field changes your app needs.
  • Delete: there is no query, because the resource no longer exists. Use the identifiers in query_variables.

For a business event such as an order becoming paid, identify both the change and the resulting state. Use triggers to detect the change and query_filter to check the current result. A query filter cannot detect a transition by itself. If your app tracks a set, subscribe to the changes that remove resources as well as the ones that add them. Split subscriptions when changes need different IDs or query roots.

Step 3: Optimize the Query

Events subscription queries have a complexity limit of 100 points. By default scalar and enum fields cost zero, object fields cost one, and connection costs depend on first and last. Shopify’s starter queries for orders and products are broad and can exceed the limit, so trim them. Keep only the fields your handler and filter need. A page size such as first: 250 limits records returned, but it is not a budget or a guarantee of a complete result.

Shopify checks complexity when you deploy an app version, without running the query. To validate without releasing, use the flag below.

shopify app deploy --no-release

A Worked Example

An app that syncs prices needs deliveries only when a variant’s price changes on an active product. Check the Product reference for the exact trigger names before you copy this.

[events]
api_version = '2026-10'

[[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 status }
}
'''
query_filter = "product.status:'ACTIVE'"

Notice that the query asks for one variant, using the child ID the trigger provides, instead of reloading every variant. Shopify recommends this: when a trigger supplies a child ID, query the changed child directly.

Which Shopify Webhooks to Keep Instead of Events

Shopify’s guide is explicit about several cases where Events do not fully replace a webhook in 2026-10.

  • returns/request and returns/approve: keep them when you need a notification per request or approval. The Events create action covers both requested and already-approved returns, so the queried status cannot reliably tell them apart.
  • reverse_fulfillment_orders/dispose: there is no equivalent disposition trigger.
  • orders/edited: there is no direct field-level equivalent. Start from the order update mapping and find triggers that cover the edits you need.
  • Tag events: the query returns the full current tag set, and a query filter cannot tell an addition from a removal. Compare with your stored state.

Our Analysis: How to Migrate Webhooks to Events

Migrate one workflow, then measure. Shopify recommends tracking deliveries received, payload size and follow-up API calls before and after. If those numbers do not drop, the subscription is probably a copy of the old payload and not a redesign.

Do not recreate the old payload. Shopify warns that copying your existing payload into a GraphQL query carries over the work your app already did. Start from what the app must do when something changes.

Treat the query result as current state. The documentation says Events queries return the state at the moment the query runs, which can differ from the state when the change happened. Use fields_changed to understand the change and data for context. Our advice is to avoid logic that assumes the two always agree.

Avoid double processing during a parallel run. If you keep a webhook and an Events subscription for the same change while you compare them, only one should act. Our suggestion is to let the old webhook act and have the new subscription log, then switch.

Keep the plumbing. Retries, authentication and deduplication still matter. Our webhook setup and debugging guide and idempotency guide still apply.

FAQ about Migrating Shopify Webhooks to Events

Will my existing webhooks stop working? No. Shopify says existing classic webhook integrations continue to work, and you can use both in one app.

Can I migrate every topic? Not yet. For topics or subscription patterns Events does not support, keep using classic webhooks. Shopify lists specific returns and fulfillment cases above.

What is the query limit? 100 complexity points per subscription query, checked when you deploy an app version.