I have a question regarding tracking changes to a store’s plan in a Shopify app.
When the app is installed, I save the store’s plan information in my database using the API (version 2025-07) and the new publicDisplayName field, according to the latest update:
Currently, to detect plan changes, I’m listening to the shop/update webhook (version 2025-07), where there’s a plan_display_name field. The value I receive there is, for example, Developer Preview.
However, the value in my database (from the Shopify API) is, for example, Development.
I wanted to monitor plan changes by comparing the value from the webhook (plan_display_name) with what I have stored in the DB from the publicDisplayName field, but these values are always different—so I would always detect a “plan change,” even if nothing actually changed.
Technically, I could query the Shopify API for the latest plan info every time a webhook arrives, but that doesn’t seem efficient and would result in unnecessary API calls.
How are you handling this?
Is there a recommended way to reliably detect real plan changes, or a mapping between these values, now that publicDisplayName has been introduced?
Or is there an official best practice for tracking store plan changes on Shopify after this update?
How important is it to you that the plan change needs to be recorded/responded to in real time?
I agree that for the most accurate change detection, you’d need to query the shop details. However, unless you debounce, the shop/update webhook could fire several times consecutively and worst case scenario exhaust your rate limit bucket.
No bueno.
If timing isn’t critical, perhaps set up a daily poll to query the shop details and update your DB record accordingly?
Hey folks - just agreeing with @Dylan here - in most cases, merchant’s plans don’t change too frequently.
You could use the shop/update webhook, but I think the most resource focused method would be to do a regular poll like Dylan suggested.
You could even run a poll every 6 hours if you did want to capture the plan changes more regularly with a simple query like this and just have it run like a cron job:
Hi @sebastian.pisula, thanks for creating this topic — I was about to ask the exact same question.
As for the two replies above asking why tracking plan changes matters: in my experience, it’s mainly to detect when a merchant pauses, cancels, or otherwise stops using Shopify, so we can handle related business logic for merchants who are no longer active.
The annoying part is that REST is now legacy, but the webhook still uses the same structure as REST. Shopify encourages us to use GraphQL, but the data between GraphQL and the webhook (REST) isn’t synchronized. How can we map values between the two? I guess someone with more experience might have the answer.
The store’s plan is very important for us, because our app has functionality that depends on it. We need this information in real time – for example, when a store changes from a development plan to any paid plan, our app must immediately require the merchant to select a plan.
Right now we handle this like this:
During app installation we fetch the current plan via GraphQL and store it in our database.
We listen to the shop/update webhook and compare what comes in the webhook with what we have in the DB.
We’ve partially solved the discrepancies between the webhook’s plan_display_name and GraphQL’s publicDisplayNameby creating a mapping like this:
But we’ve recently noticed some strange cases that are difficult to handle:
The webhook sends Basic, while GraphQL returns Other. At the same time, we have other shops in the DB that are correctly on the Basic plan. We cannot map Basic => Other nor Other => Basic.
Similarly: the webhook sends Advanced, while GraphQL returns Other. But we have other shops in the DB correctly on the Advanced plan.
In such cases, we’re left without consistency and it’s unclear how to handle this safely.
The best solution would be if the webhook sent the same standardized plan names as the GraphQL API.
This scenario is introducing issues for our application as well.
A simple solution (at least as an outsider looking in) would be to add a plan_public_display_name field to the shop/update webhook.
If this field could contain the same values that the GraphQL’s shop.plan.publicDisplayName does, that would save all of us the pain of trying to map the plan_display_name field (which doesn’t have documented values by the way) to the public display name from the GraphQL API.
Of course, this would result in 3 different plan name fields in the shop webhook - plan_name, plan_display_name, and plan_public_display_name - but I don’t see another reasonably simple solution.
I would echo @iam_parker’s sentiment that webhook definitions being based on the deprecated REST structure can be annoying. Shopify had a beautiful thing going when whatever you fetched via the REST API would be the same thing you received in the webhook for that resource. Obviously the dynamic nature of GraphQL makes that a challenge to carry forward and it’s not feasible to render every field - but when there is a useful field on a resource in the GraphQL API that does not have a clear mapping to a field in the webhook structure for that same resource, it makes it quite difficult to have an application that responds to real-time events.
I would like to see the payloads match as well, we use this to detect plan changes as well and features depend on it being accurate, at the very least, we would like to see a partnerDevelopment field returned in the webhook so we can work out if the store is on a dev plan or not still. It’s great that Shopify is introducing new things, but i would like to see this carried across all of Shopify consistently.