githubEdit

Custom Shopify webhooks

Filter Shopify webhooks in Mechanic, customize Shopify webhook payloads, include metafields, and receive metaobject webhook events.

Custom Shopify webhooks let Mechanic receive Shopify webhook deliveries with Shopify-side filters, customized payload fields, metafields, and metaobject events. They are an advanced Shopify event option.

Use them when you want Shopify to filter webhook deliveries, send a smaller payload, include metafields in the webhook payload, or deliver metaobject webhook events. Mechanic receives matching deliveries and turns them into custom user/... events, which your tasks subscribe to like any other event topic.

circle-info

Custom Shopify webhooks are an advanced tool. Most Shopify-triggered tasks should use regular shopify/... subscriptions. Reach for a custom Shopify webhook when the Shopify-side behavior matters before the event reaches Mechanic.

At a glance

Question
Answer

Should most tasks use this?

No. Most Shopify-triggered tasks should use native shopify/... subscriptions.

What does a task subscribe to?

The custom Mechanic topic, like user/products/active_update.

Where is the original Shopify topic?

event.shopify_topic, like shopify/products/update.

What is in event.data?

The Shopify webhook payload after Shopify applies the filter and payload customization.

Can this receive metaobject webhooks?

Yes, for shopify/metaobjects/create, shopify/metaobjects/update, and shopify/metaobjects/delete, with an explicit type: filter.

How routing works

A custom Shopify webhook connects one Shopify webhook topic to one Mechanic topic:

Shopify topic:  shopify/products/update
Mechanic topic: user/products/active_update
Filter:         status:active

When Shopify sends a matching products/update webhook, Mechanic creates an event on user/products/active_update. Your task subscribes to that Mechanic topic, not shopify/products/update. In Liquid, event.topic is the Mechanic topic, and event.shopify_topic keeps the source Shopify topic.

When should I use custom Shopify webhooks?

If your task can receive the normal Shopify webhook payload and decide what to do in Liquid, use a regular native shopify/... subscription.

Use a custom Shopify webhook when the task needs Shopify to do something before delivery:

  • send only matching resources, like active products

  • deliver a smaller payload with Include fields

  • filter by, or include, specific metafields

  • receive Shopify metaobject webhooks

Custom Shopify webhooks are different from Mechanic webhooks, which are public inbound URLs for external systems like forms, ERPs, CRMs, and fulfillment services.

How do I create a custom Shopify webhook?

Open Settings, choose Custom Shopify webhooks, and click New custom Shopify webhook.

Custom Shopify webhooks in Mechanic settings, showing Shopify topics routed to user topics

Fill in:

  • Name β€” a label for your reference.

  • Mechanic topic β€” the user/... topic your tasks will subscribe to, e.g. user/products/active_update. Use two parts after user/, with lowercase letters, numbers, and underscores.

  • Shopify topic β€” the Shopify webhook topic, e.g. shopify/products/update. The picker in Settings is the source of truth for supported topics.

  • Payload customization area β€” at least one of Filter, Include fields, Metafield namespaces, or Metafields. Filter controls whether Shopify delivers the webhook; the other fields control the payload Shopify sends.

A custom Shopify webhook form for Shopify metaobject updates with a type filter

After saving, create or update a task so it subscribes to the Mechanic topic. Until an enabled task subscribes, the webhook may be enabled but Not receiving.

Subscription offsets work too:

In the task editor, Mechanic shows when a user/... subscription is backed by a custom Shopify webhook. It also warns about inactive or unknown custom webhook topics, and about native shopify/... subscriptions that would conflict with an enabled unfiltered custom webhook.

The Mechanic task editor showing a user topic backed by a custom Shopify metaobject webhook

Which Shopify webhook topics can I use?

Use the Shopify topic picker in Settings for the current supported list. It includes supported Shopify webhook topics plus the metaobject topics covered below.

The Event topics reference lists native shopify/... task subscriptions. Custom Shopify webhooks are configured separately, and they deliver onto user/... topics.

How do I filter Shopify webhooks in Mechanic?

Add a Shopify webhook filter to the custom webhook. Shopify evaluates the filter as part of the webhook subscription before Mechanic receives the delivery.

Examples:

Filters use Shopify's webhook filter syntax. Nested fields use dots, boolean operators like AND, OR, and NOT are supported, and values with spaces should be quoted. For arrays of objects, Shopify matches when at least one nested object satisfies the filter.

circle-exclamation
circle-info

Shopify filters are state filters, not "changed field" filters. A products/update filter like variants.price:>=10.00 means the product currently has a variant at that price or higher. It does not mean the variant price changed in this update.

When a filter does not behave as expected

Shopify filter problems do not all fail the same way:

  • Incorrect syntax is usually rejected when you save.

  • A field path that does not exist for that Shopify topic can save, but never match.

  • A value compared with the wrong type, like an unquoted exact money value, can save, but never match.

  • A field used in Filter but missing from Include fields, when Include fields is set, should be rejected when you save.

  • A metafield used in Filter but missing from Metafield namespaces or Metafields should be rejected when you save.

  • A matching update that only changed fields outside Include fields may be debounced if the trimmed payload would be the same as a recent delivery.

When troubleshooting, inspect a real Shopify webhook payload for the topic, then build the filter from fields that are present in that payload. For tags on topics like products and orders, treat the tag list as string-like: filter for the tag keyword Shopify sends, and test with a real event.

Filtering by metafields

To filter by metafield data, select the metafield in Metafield namespaces or Metafields, then use metafields.namespace, metafields.key, and metafields.value in the filter:

For a value that only needs to exist:

For variant metafields on a product webhook, prefix the metafield path with variants.:

Metafield filters use Shopify webhook filter syntax, not Shopify Admin search syntax. For example, use metafields.namespace:custom AND metafields.key:track_with_mechanic; do not use a GraphQL search query shape like metafields.custom.track_with_mechanic:true.

Mechanic runs a local preflight check for common filter mistakes, then Shopify validates the subscription on save. If you also set Include fields, include every field your filter references. For example, a filter using variants.price needs variants or variants.price in Include fields. For metafield filters, choose the namespace or exact metafield in Metafield namespaces or Metafields. If Include fields is set, include metafields too.

For Shopify's full syntax and edge cases, see Filter your eventsarrow-up-right.

How do I receive Shopify metaobject webhooks in Mechanic?

Create a custom Shopify webhook using one of these Shopify topics:

  • shopify/metaobjects/create

  • shopify/metaobjects/update

  • shopify/metaobjects/delete

Metaobject webhooks require a type: filter. Shopify does not support type:*, so list the exact metaobject definition type you want.

To listen for more than one type:

Example configuration:

Task subscription:

Task code:

Native Mechanic shopify/... subscriptions do not include metaobject topics. Use custom Shopify webhooks for metaobjects.

How do I customize a Shopify webhook payload?

Use the payload customization fields:

  • Include fields β€” sends only specific payload fields, such as id, updated_at, or variants.price.

  • Metafield namespaces β€” includes all metafields in a namespace, such as custom.

  • Metafields β€” includes exact metafields, such as custom.pack_size.

triangle-exclamation

When Include fields is blank, Shopify sends the normal payload shape. When Include fields has values, Shopify sends only those fields.

For update topics, include updated_at when you want every matching update to arrive. Shopify may skip repeated deliveries when Include fields produces the same trimmed payload as a recent delivery. Leave updated_at out when you intentionally want Shopify to reduce repeat deliveries where only fields outside your payload changed.

For Shopify's payload rules, see Modify your payloadsarrow-up-right.

The metafields gotchas

include_fields is not a metafield selector. This will not include the custom.pack_size metafield:

Use Metafields for exact metafields:

circle-exclamation

If you are also using Include fields, add metafields there too:

Recipes

Active product updates

Use this when a task only cares about active products.

Task subscription:

Task code:

Product updates with a smaller variant payload

Use this when the task only needs variant IDs and prices.

Remember: this filter means the product has at least one variant priced at or above 10.00. It does not prove the price changed in this specific update.

Product updates filtered by a metafield

Use this when only products with a known metafield should reach the task.

Task code:

This is a state filter. The task receives product updates while the metafield has this value; the filter does not prove the metafield changed in that update.

What the task receives

For a custom Shopify webhook event:

  • event.topic is the custom Mechanic topic, e.g. user/products/active_update.

  • event.shopify_topic is the source Shopify topic, e.g. shopify/products/update.

  • event.data is the Shopify webhook payload after filtering and payload customization.

  • event.source is custom_shopify_webhook_subscription:<uuid>.

Mechanic also builds the usual Shopify subject variable from the source Shopify topic. A custom shopify/products/update webhook provides product, shopify/orders/create provides order, and shopify/metaobjects/update provides metaobject. These variables start with the payload Shopify delivered, so Include fields and metafield settings affect what is immediately present.

For native Shopify deliveries, event.topic and event.shopify_topic are the same. For non-Shopify events, event.shopify_topic is nil.

Delivery statuses

Status
What it means

Draft

The custom Shopify webhook has not been saved yet.

Receiving

It is enabled, synced with Shopify, and at least one enabled task subscribes to its Mechanic topic.

Not receiving

It is enabled, but no enabled task subscribes to its Mechanic topic yet.

Needs permissions

Mechanic needs additional Shopify permissions before Shopify can activate the subscription.

Needs filter

The webhook has no filter and conflicts with a native shopify/... task subscription for the same Shopify topic.

Sync error

Shopify rejected the last sync attempt, or cleanup failed. Read the inline error and adjust the configuration.

Disabled

Mechanic keeps the configuration, but does not keep a matching Shopify subscription active.

Common issues and gotchas

Tasks subscribe to the Mechanic topic. If the webhook sends to user/products/active_update, the task must subscribe to user/products/active_update. Subscribing to shopify/products/update still means native, unfiltered delivery.

Enabled does not always mean receiving. A custom Shopify webhook is only kept active in Shopify while at least one enabled task subscribes to its Mechanic topic.

Blank filters can conflict with native subscriptions. Shopify cannot keep an unfiltered native subscription and an unfiltered custom subscription for the same app, destination, and topic. This includes native task subscriptions and Shopify webhooks Mechanic keeps for platform behavior. Add a real filter, or use id:* when you need every delivery. For metaobjects, use a concrete type: filter instead.

Some money fields behave like strings. Shopify webhook payloads often represent prices and totals as strings, like "0.00". For exact money matches, quote the decimal string in the Shopify filter, e.g. total_price:"0.00" or variants.price:"0.00". Test against a real Shopify event when exact field typing matters.

Filter problems fail in different ways. Shopify rejects some filter problems when you save, while others save but never match a delivery. See When a filter does not behave as expected.

Include fields can affect repeat update deliveries. Include fields is not a "fire only when these fields change" switch, but Shopify may skip a recent update delivery when the payload after trimming would be identical. Include updated_at when you want every matching update to arrive. Leave it out when you intentionally want Shopify to reduce repeat deliveries where only fields outside your payload changed.

Payload customization does not make a webhook unique. Shopify only allows one enabled webhook for the same app, destination, Shopify topic, and filter. Two custom Shopify webhooks with the same topic and filter still conflict even if the Name, Mechanic topic, Include fields, Metafield namespaces, or Metafields are different. This is a Shopify limitation, and it often appears after duplicating a webhook with import/export.

Two webhooks can share one Mechanic topic. This is allowed, but tasks subscribed to that topic will receive deliveries from both webhooks. Use event.shopify_topic and event.source if the task needs to tell them apart.

Preview mode is not an end-to-end webhook test. Task preview runs Liquid against preview data. To confirm a Shopify filter or payload shape, save the custom webhook and trigger a real matching event in Shopify.

Manage these in Mechanic. Custom Shopify webhooks are owned by Mechanic's sync process. Create, edit, disable, and delete them from Settings β†’ Custom Shopify webhooks.

Do not look for them in Shopify Admin's webhook list. Mechanic's custom Shopify webhooks are EventBridge-backed app subscriptions. Use the delivery status in Mechanic to confirm whether a webhook is receiving.

Why isn't my custom Shopify webhook receiving?

Check these first:

  1. Is at least one enabled task subscribed to the custom Mechanic topic?

  2. Does the webhook show Needs permissions? Mechanic should prompt you to update Shopify access when it detects missing scopes. If a needed scope is not being detected from the task or webhook, declare it in the subscribed task with the permissions tag.

  3. Does the webhook show Needs filter? Add a filter, even id:* for non-metaobject topics.

  4. Does the task subscribe to the user/... topic exactly?

  5. Did you trigger a real Shopify event that matches the filter?

  6. Does the filter use fields that exist for that Shopify topic, with the value types Shopify sends?

  7. If Include fields is set, would the trimmed payload differ from a recent delivery, or did you include updated_at?

Open the resulting event in Mechanic and check event.data, event.topic, and event.shopify_topic. The event detail page also shows the custom Shopify webhook source when Mechanic knows which webhook delivered it.

Import and export

Custom Shopify webhooks can be imported and exported from the custom Shopify webhooks settings area.

Exports include configuration only: name, shopify_topic, event_topic, filter, include_fields, metafield_namespaces, and metafields. They do not include enabled state, Shopify subscription IDs, sync status, subscriber counts, or delivery status.

List imports are created disabled for review. Imports into an existing webhook keep that webhook's current enabled or disabled state.

If an imported webhook is a variant of an existing webhook, give it a different Filter before enabling it. Changing only payload customization is not enough for Shopify to treat it as a separate subscription.

Last updated

Was this helpful?