I’m running into a weird one with a filtered products/update
webhook and would love a sanity check from the community.
What I’m trying to do
I want products/update
to fire only when the product has a metafield custom.trigger = true
. Simple enough. The catch: sometimes we save multiple metafields at once (e.g., custom.colors
, custom.shape
) together with custom.trigger
.
What actually happens
-
If I set only
custom.trigger = true
and save → the webhook arrives as expected. -
If I save
custom.trigger = true
and add/update other metafields in the same save (e.g., colors/shape) → no webhook is delivered. -
If I remove the filter completely, I get
products/update
on any change (so the endpoint is fine).
Environment
-
Admin API: GraphQL
2025-01
-
Topic:
PRODUCTS_UPDATE
-
Metafields are product-level (not variant)
-
Namespace:
custom
-
Callback: Laravel endpoint that just logs and queues a job (working fine)
$variables = [
"topic" => "PRODUCTS_UPDATE",
"webhookSubscription" => [
"callbackUrl" => "https://xxxxx.com/xxx/xxx/xxx/update",
"format" => "JSON",
"metafieldNamespaces" => ["custom"],
"filter" => "metafields.namespace:custom AND metafields.key:trigger AND metafields.value:true"
]
];
public function handle(Request $request)
{
$topic = $request->header('X-Shopify-Topic');
$shopDomain = $request->header('X-Shopify-Shop-Domain');
$webhookId = $request->header('X-Shopify-Webhook-Id');
$payload = $request->json()->all();
Log::info('Shopify webhook received', compact('topic','shopDomain','webhookId') + ['payload'=>$payload]);
// persist + queue job...
return response('OK', 200);
}
What I already tried
-
Kept the filter in the “explicit” form above (namespace/key/value).
-
Tested with and without
includeFields
(and with/withoutmetafields
included there). -
Double-checked that
custom.trigger
is product-level, not variant. -
Read back the stored subscription with:
query {
webhookSubscriptions(first: 50) {
edges { node { id topic filter includeFields metafieldNamespaces } }
}
}
Everything matches what I create.
Repro steps
-
Create a product-level metafield
custom.trigger
(boolean). -
Save product with
custom.trigger = true
→products/update
arrives -
Now, in the same save, set
custom.trigger = true
and add/update other product metafields likecustom.colors
orcustom.shape
→ no webhook
I’ve attached a screenshot in my admin to illustrate: when only trigger=true
exists, the webhook fires; as soon as I also add Colors/Shape (while keeping trigger
true), it stops firing.
Questions
-
Is the metafield filter on
products/update
evaluated only against metafields that are present/changed in that specific event? -
If so, shouldn’t
trigger
still be present when I set it totrue
in the same save as other metafields? -
Do I need
includeFields: ["metafields"]
for metafield filtering to work reliably, or is that unrelated? -
Has anything changed recently in how metafield writes are aggregated into
products/update
vs.metafields/update
? -
If my rule is “deliver whenever the product has
trigger=true
(regardless of what else changed)”, is the recommended approach to avoid the filter and just checktrigger
server-side after receiving an unfilteredproducts/update
?
Happy to provide a webhook_id
and timestamps if that helps. Thanks a ton for any pointers!