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 has two kinds of subscription. They differ in where you declare them, which shops they cover and what happens when deliveries fail.
shopify.app.tomlThe GraphQL Admin API (webhookSubscriptionCreate), often from your app’s afterAuth hookApps 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 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.
orders/createShopify 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.
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.
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.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-Idand skip the ones you have already processed. - Run a reconciliation job that fetches what changed since its last run, with the
updated_atfilters 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 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.
- 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.
Sources: Shopify’s pages on webhooks, managing subscriptions, the delivery structure, verifying deliveries, troubleshooting and Events and webhooks. Meridian’s Pub/Sub and forwarded webhooks documentation. All checked on 4 November 2026.
