Webhook filter by metafield

Hey all,

Is it possible to add a webhook filter to the products/update topic, where it filters based on a product metafield value?

The docs don’t clearly say whether you can or not.

In short, my app only cares about updates to certain products. Products that the merchant has selected within my app. I want to be able to set something on those products so that I can then apply a webhook filter.

The problem at the moment is my server is receiving probably 5000x more webhooks than is necessary, because I get every product update. At times, I get hit with thousands at once, when the merchant does a bulk update across all products (some stores seem to do automated bulk updates across tens of thousands of products multiple times a day).

Firstly, please spare me the “your infrastructure should be set up to handle that easily! it’s a developer problem” :rofl:

That aside, I would still like to keep things efficient. If there’s a way I can reduce the number of requests my server receives by a magnitude of thousands, why wouldn’t I? Moving on…

I’m seeing inconsistencies in what properties can be defined in the webhook subscription depending on the way it’s done.

It seems that the toml doesn’t support metafieldNamespaces.

It seems that registerWebhooks in the remix template doesn’t support filter.

I’ve resorted to using regular graphql so that both metafieldNamespaces and filter can be defined.

I’ve set up a webhook like so:

 "webhookSubscription": {
    "format": "JSON",
    "includeFields": ["id", "metafields", "status"],
    "metafieldNamespaces": ["internal"],
  }

This returns me a nice payload, e.g.:

{
  "id": [redacted],
  "metafields": [
    {
      "id": [redacted],
      "namespace": "internal",
      "key": "superapp_product",
      "value": true,
      "description": null,
      "owner_id": [redacted],
      "created_at": "2025-06-15T19:39:09+00:00",
      "updated_at": "2025-06-15T20:41:16+00:00",
      "owner_resource": "product",
      "type": "boolean",
      "admin_graphql_api_id": "gid://shopify/Metafield/[redacted]"
    }
  ],
  "status": "active"
}

However, updating the webhook subscription to include a filter like the one below, results in the webhook not firing:

filter: "metafields.namespace:internal"

I’m wanting to eventually filter on:

filter: "metafields.namespace:internal AND metafields.key:superapp_product AND metafields.value:true

I’ve also tried a different syntax:

filter: "metafields.internal.superapp_product:true"

Because this works when you do a graphql products query like so:

query getProductsByMetafieldValue {
  products(first: 5, query: "(metafields.internal.prp_product:true" ) {
    edges {
      node {
        id
        title
        metafields (first: 10) {
          edges {
            node {
              namespace
              key
              value
            }
          }
        }
      }
    }
  }
}

I guess the alternative is to use tags, but I don’t love that idea, given they’re easy for a merchant to modify/remove unintentionally within the Shopify UI.

Has anyone discovered a way to filter webhooks by metafield? If so, I’d love to see your syntax.

Alternatively, does anyone have any other suggestions for reducing the number of webhooks fired in this scenario? I’ve mentioned tags, but I wonder if there’s other alternatives, that are less visible to the merchant. I’m looking to reduce the number of webhooks fired only - not ways to better manage the volume of webhooks.

Thanks all!

Ok so it turns out it does work!

You need to set includeFields and include metafields within it. I was doing this, but where it was going wrong is that I was setting includeFields to a small set for testing - i.e. id, metafields, status.

Then to try to fire the webhook, I was updating the product title. Shopify wasn’t firing the webhook as nothing changed on the product with respect to my includeFields. Adding title into the set ensured it fired each time.

I recall reading that you can’t use includeFields to tell Shopify to only fire webhooks when those fields changed. However I think Shopify will avoid firing a webhook if nothing has changed in your includeFields within a short timeframe (timeframe unknown).

A safer way to go about it is probably just to include updated_at within your includeFields, then every change should always fire a webhook (provided it passes your filters).

So the final settings that seem to work, are:

  "webhookSubscription": {
    "format": "JSON",
    "includeFields": ["id", "metafields", "status"],
    "metafieldNamespaces": ["internal"],
    "filter": "metafields.value:true AND metafield.key:superapp_product AND metafield.namespace:internal",
    "callbackUrl": "url_here"
  }

I still don’t think this is possible to do via the toml, or via registerWebhooks in the remix template. If I’m correct there, it kinda sucks, but overall if it’s working with a graphql mutation I’m still happy.

If anyone has more knowledge, please feel free!

.

Edit: You don’t actually need includeFields to be specified. If you set metafieldNamespaces then it will automatically include metafields in the payload, and that’s enough to be able to use filters on it.

1 Like

Glad you figured this out and thanks for posting up your solution!

@Liam-Shopify any word on metafieldNamespaces or metafields being supported in the TOML?

Per this page, these options are missing: App configuration

They’re available via graphql admin API:

Nothing to share on that right now @gunner