# Shopify webhooks: delivery, retries and failures

> How Shopify delivers webhooks (subscriptions, HMAC, the 5-second timeout, 8 retries in 4 hours), why deliveries go missing and how to handle them reliably.

Source: https://www.the-meridian.ai/blog/reliable-shopify-webhooks

[All articles](https://www.the-meridian.ai/blog)

Guide

## Shopify webhooks: how delivery works and why events go missing

How Shopify delivers webhooks (subscriptions, HMAC, the 5-second timeout, 8 retries in 4 hours), why deliveries go missing and how to handle them reliably.

By [Matthias Gayaud](#author), Founding Engineer at Meridian · Updated 4 November 2026

Shopify webhooks tell your app when something changes in a store, such as a new order. Your app subscribes to topics like `orders/create`, and Shopify sends each event to an HTTPS endpoint, a Google Cloud Pub/Sub topic or an Amazon EventBridge bus. Over HTTPS, your endpoint must answer with a 2xx within 5 seconds. Otherwise the delivery fails and Shopify retries it.

**In short.** Shopify doesn’t promise to deliver every webhook, so plan for gaps. Declare subscriptions in `shopify.app.toml`, where failing deliveries don’t get them deleted. Answer within 5 seconds and do the work from a queue. Skip duplicates by their `X-Shopify-Webhook-Id`, and run a reconciliation job that fetches what changed from the API.

### How does Shopify deliver webhooks?

A subscription pairs one or more topics with a destination. A topic is a resource and an action, such as `products/update` or `app/uninstalled`, and each one needs the access scope that goes with it. When a qualifying change happens in a store, Shopify sends the payload, by default the full REST resource, with headers that describe the delivery.

> Shopify recommends using Google Pub/Sub as a cloud-based solution for delivering webhooks. You can also use Amazon EventBridge. If you prefer to build your own webhooks infrastructure, you can deliver through HTTPS, but there are extra considerations to take into account.

Shopify, Manage webhook subscriptions

Shopify has two kinds of subscription. They differ in where you declare them, which shops they cover and what happens when deliveries fail.

|  | App-specific | Shop-specific |
| --- | --- | --- |
| Declared in | shopify.app.toml | The GraphQL Admin API (webhookSubscriptionCreate), often from your app’s afterAuth hook |
| Applies to | Every shop that installs the app | One shop at a time |
| Compliance topics | Supported | Not through the Admin API |
| After repeated failures | Kept | Deleted by Shopify |
| When to use | By default, as Shopify recommends | Topics, destinations or filters differ between shops |

Apps on the Shopify App Store must also subscribe to the three mandatory compliance topics, `customers/data_request`, `customers/redact` and `shop/redact`. The [guide to Shopify’s protected customer data requirements](https://www.the-meridian.ai/blog/shopify-protected-customer-data) covers what they ask of your app.

On HTTPS, each delivery carries these headers. On Pub/Sub and EventBridge, the same metadata comes inside the message.

| Header | What it carries |
| --- | --- |
| X-Shopify-Topic | The topic, such as orders/create |
| X-Shopify-Hmac-Sha256 | The base64 HMAC signature, on HTTPS only |
| X-Shopify-Shop-Domain | The store's myshopify.com domain |
| X-Shopify-API-Version | The API version that serialized the payload |
| X-Shopify-Webhook-Id | Identifies the delivery, to skip duplicates |
| X-Shopify-Triggered-At | When Shopify triggered the webhook |
| X-Shopify-Event-Id | Shared by every delivery that comes from the same merchant action |

Shopify also offers Events, a newer subscription mechanism with field-level triggers and custom GraphQL payloads, for a subset of topics. Webhooks remain supported, and the two can run side by side in the same `shopify.app.toml`.

### Timeouts, signatures and retries

Over HTTPS, your endpoint has 1 second to accept the connection and 5 seconds to answer the whole request. Shopify verifies your SSL certificate, and any answer outside the 2xx range counts as a failure, 3xx redirects included.

Each delivery is signed. `X-Shopify-Hmac-Sha256` holds a base64 HMAC-SHA256 of the raw request body, keyed with your app’s client secret. Your app computes the same value before it parses the body, and rejects any delivery where the two don’t match. Shopify’s React Router app template does this for you in `authenticate.webhook`.

When a delivery fails, Shopify retries it 8 times over the next 4 hours, waiting longer before each try. After 8 consecutive failures, it deletes the subscription if your app created it through the Admin API, and emails the app’s emergency developer address.

The same webhook can arrive twice, after a timeout or a retry. Order isn’t guaranteed either, within a topic or across topics for the same resource.

> Your app shouldn’t rely on receiving data from Shopify webhooks. Webhook delivery isn’t always guaranteed, and your app can miss or mishandle events for other reasons, such as handler failures or downtime.

Shopify, About webhooks

### Why do Shopify webhooks go missing?

A missing webhook was refused by your endpoint, had no subscription on that shop, or was dropped by Shopify. A late delivery looks missing too.

| Cause | What happens | Fix |
| --- | --- | --- |
| A slow handler | Your app does the work before it answers and goes past 5 seconds. The delivery fails and waits for a retry. | Answer first and do the work afterwards, from a queue. |
| A redirect | Shopify counts any 3xx as a failure, such as a redirect from HTTP to HTTPS, to a URL with a trailing slash, or to a login page. | Subscribe the exact URL that answers. |
| A failed HMAC check | Your app parsed the body before checking the signature, or holds the wrong client secret, and answers 401 to real deliveries. | Verify the raw body with the current secret. After a rotation, Shopify can take up to an hour to sign with the new one. |
| A deleted subscription | After 8 consecutive failures, Shopify deletes a subscription your app created through the Admin API. That shop gets nothing more on the topic. | Fix the endpoint, then create the subscription again. |
| No subscription on that shop | A shop-specific subscription exists only where your code created it. A topic added to the code later is missing on shops that installed before. Each topic also needs its access scope. | List each shop's subscriptions and create the missing ones. |
| A quick reinstall | A store that reinstalls within about a minute of uninstalling finds the previous install's subscriptions still in place, and Shopify deletes them shortly after. | Register the store's topics again a few minutes after the uninstall. |
| A debounced payload | With include\_fields, two events can produce the same payload. Shopify drops the later one when they arrive close together. | Include a field that always changes, such as updated\_at. |
| Late or out of order | Order isn’t guaranteed, so products/update can arrive before products/create, and deliveries can be delayed. | Compare X-Shopify-Triggered-At or updated\_at before you write, so an older event never overwrites a newer one. |

To find which one you have, open your app’s Logs in the Dev Dashboard. They list each delivery and your app’s response for the past 7 days, up to several minutes behind. A response code points at your handler. No delivery at all points at the subscription or its filter.

### Handling webhooks reliably

- Declare your subscriptions in `shopify.app.toml`, unless their topics, destinations or filters must differ between shops.
- Or deliver to Pub/Sub or EventBridge. The 5-second timeout and the HMAC check then no longer apply to your endpoint, and you run the consumer that reads from the bus instead.
- Put the work on a queue and answer as soon as the signature checks out, so a slow database or a call back to Shopify never pushes you past 5 seconds.
- Make each handler idempotent. Where it can’t be, store every `X-Shopify-Webhook-Id` and skip the ones you have already processed.
- Run a reconciliation job that fetches what changed since its last run, with the `updated_at` filters many GraphQL queries accept. It recovers what the webhooks missed, including after an outage.
- Watch the delivery metrics on your app’s Overview in the Dev Dashboard. Shopify flags a failure rate above 0.5% as higher than average, and response times of 4 to 5 seconds as a timeout risk.

**Good to know.** `shopify app webhook trigger` sends a sample delivery of any topic to any of the three kinds of destination. It goes straight to the address you give it, so it tests your handler, not your subscription. Run it before you change `webhooks.api_version`, which sets the payload version of every app-specific subscription.

### Receiving webhooks through Meridian

Meridian hosts Shopify apps and receives their webhooks over Google Cloud Pub/Sub, then forwards each event to a route in your app, `/webhooks/shopify` by default. Meridian re-serializes the event, so your app [checks Meridian’s signature](https://help.the-meridian.ai/sdk-webhooks) instead of Shopify’s HMAC. That signature covers the shop, the topic, the webhook ID and the body.

- Meridian keeps each store’s subscriptions in sync as stores install and uninstall your app. For an app hosted on Meridian, the Webhooks tab shows for each topic whether every store accepted the subscription, with the reason when one didn’t. A store that reinstalls quickly gets its topics registered again, at 2 and at 10 minutes after the uninstall.
- A live delivery log lists each delivery with its topic, shop, status, attempts and your app’s response code. Your coding agent can read the same routes and deliveries through [Meridian MCP](https://help.the-meridian.ai/mcp-server).
- An error other than a timeout, a connection error, a 5xx, 408, 425 or 429 is final after one try. A 404 or a 401 isn’t retried, and the topic’s row on the Webhooks tab shows the status your app returned.
- An app hosted elsewhere can receive forwarded events too. You declare its domain in Meridian, and your app proves it holds the signing secret by answering a signed check.

For the errors it does retry, Meridian makes two more tries, 10 then 30 seconds apart. That window is far shorter than Shopify’s 4 hours over HTTPS, and a delivery that failed all three tries isn’t sent again. Keep the reconciliation job.

[See Meridian hosting](https://www.the-meridian.ai/hosting) [Read the Pub/Sub docs](https://help.the-meridian.ai/pub-sub)

Sources: Shopify’s pages on [webhooks](https://shopify.dev/docs/apps/build/webhooks), [managing subscriptions](https://shopify.dev/docs/apps/build/webhooks/subscribe), [the delivery structure](https://shopify.dev/docs/apps/build/webhooks/delivery-structure), [verifying deliveries](https://shopify.dev/docs/apps/build/webhooks/verify-deliveries), [troubleshooting](https://shopify.dev/docs/apps/build/webhooks/troubleshoot) and [Events and webhooks](https://shopify.dev/docs/apps/build/events-webhooks). Meridian’s [Pub/Sub](https://help.the-meridian.ai/pub-sub) and [forwarded webhooks](https://help.the-meridian.ai/sdk-webhooks) documentation. All checked on 4 November 2026.

Written by  
Matthias Gayaud, Founding Engineer at Meridian

Works across the Meridian platform, from Plan Builder and the SDK to webhooks and automations.

[LinkedIn](https://www.linkedin.com/in/matthias-gayaud/) [The Meridian team](https://www.the-meridian.ai/about#matthias)

### Questions.

#### Why is my Shopify webhook not firing?

Start with the subscription. Does that shop have one for the topic, with the access scope the topic needs and a filter that lets the event through? Then look for the delivery on your app's Logs page in the Dev Dashboard, which keeps 7 days. If it is there, the response code your app returned explains the failure.

#### How long does Shopify wait for a webhook response?

It allows 1 second to connect and 5 seconds for the whole request. A timeout, a redirect or any status outside 2xx is a failed delivery, and Shopify retries it 8 times over the next 4 hours, waiting longer each time. Keep the handler short: check the signature, queue the event and answer.

#### Why did Shopify delete my webhook subscription?

Its deliveries failed 8 times in a row, and your app had created it through the GraphQL Admin API. Shopify deletes those and emails the app's emergency developer address. Subscriptions declared in shopify.app.toml are kept. Fix the endpoint, then recreate what is missing with code that lists a shop's existing subscriptions and adds only the absent ones.

#### Can Shopify send the same webhook twice?

Yes. Shopify keeps duplicates to a minimum, but a timeout or a retry can bring the same webhook back. Write handlers so a second run changes nothing, or keep the X-Shopify-Webhook-Id values you have processed and skip repeats. Events can also arrive out of order, so compare timestamps before overwriting newer data.

#### Which webhooks must a Shopify App Store app handle?

Every app distributed through the Shopify App Store must subscribe to three compliance topics: customers/data\_request, customers/redact and shop/redact. List them under compliance\_topics in shopify.app.toml or set them in the Dev Dashboard, because the Admin API can't subscribe to them. They ask your app to send or delete a customer's data, or to delete a store's data.

#### How does Meridian deliver Shopify webhooks?

Shopify sends your app's webhooks to Google Cloud Pub/Sub, not straight to your app. Meridian forwards each one, signed with your app's own secret. If your app times out or answers a 5xx or 429, Meridian tries twice more, 10 and then 30 seconds apart. Every delivery shows in a live log.

### Keep reading.  
More from the Meridian team.

[

Billing

#### Mantle alternatives for Shopify apps, job by job

Mantle shut down in 2026. What now replaces its billing, analytics, CRM, email, affiliates, help desk and SASI rank tracking, with prices from each vendor.

](https://www.the-meridian.ai/blog/mantle-alternatives)  
[

Security

#### Shopify's protected customer data requirements, explained

What Shopify counts as protected customer data, the Level 1 and Level 2 requirements, when Shopify reviews your request, and how to get ready.

](https://www.the-meridian.ai/blog/shopify-protected-customer-data)

### Ready to ship your next Shopify app?

Try Meridian free, or book a personal walkthrough.

[Start free trial](https://app.the-meridian.ai/register) [Book a demo](https://calendar.app.google/7FzmyGhWkf5ACAVr6)

14 days, no card needed.
