Skip to content
Integration

Consuming Shopify Webhooks: A Practical Integration Guide

Receive and process Shopify webhooks reliably — X-Shopify-Hmac-Sha256 verification, X-Shopify-Topic routing, fast acknowledgement, dedupe on X-Shopify-Webhook-Id, and a debugging checklist.

Published 6 min read
On this page

A Shopify store generates events all day — an order is placed, a payment captures, a customer edits their address, a merchant uninstalls your app mid-checkout. Shopify delivers these as webhooks, and for most apps they are the only reliable signal that something happened in a store you do not control. Miss an orders/paid and you never fulfil; miss an app/uninstalled and you keep billing a merchant who left. This guide covers consuming Shopify webhooks correctly: verifying them, routing on topic, and surviving the failure handling Shopify enforces on apps.

What Shopify webhooks are

A Shopify webhook is an HTTP POST sent when a subscribed event occurs in a store. The event topic — Shopify’s term for the type — arrives in the X-Shopify-Topic header (orders/create, orders/paid, app/uninstalled), the originating store in X-Shopify-Shop-Domain, a unique delivery id in X-Shopify-Webhook-Id, and the resource as a JSON body. A single endpoint typically receives several topics, so you route on the header.

Common use cases

  • Order fulfilment — react to orders/create and orders/paid to reserve stock, charge, or hand off to a fulfilment system.
  • App lifecycle — handle app/uninstalled to stop billing, revoke tokens, and clean up store data promptly.
  • Inventory and product sync — keep an external catalogue aligned from products/update and inventory-level events.
  • Compliance — Shopify requires apps to handle mandatory data-subject topics (customer data requests and redaction); consult current docs for the exact set.

Configuring the webhook in Shopify

Webhooks are created per topic, either in the Shopify admin (Settings → Notifications, for a store’s own webhooks) or, for apps, via the Admin API or the app configuration. At a high level you choose the topic, the delivery URL, and the format (JSON), and Shopify signs deliveries using a shared secret — your app’s API secret for app-created webhooks. Where and how you register depends on whether you are a store owner or an app developer.

Because the exact registration paths and the mandatory-topic requirements change, confirm current specifics against Shopify’s webhook documentation rather than assuming a fixed set of screens. Do not hard-code topic lists you have not re-checked.

Shopify expects a fast response and treats slow or failing endpoints harshly (see below), so acknowledge first and process asynchronously:

  1. Capture the raw request body — you need the exact bytes for the HMAC.
  2. Verify X-Shopify-Hmac-Sha256.
  3. Read X-Shopify-Topic and X-Shopify-Shop-Domain to route.
  4. Enqueue the work keyed by X-Shopify-Webhook-Id, and return 2xx.
  5. Fulfil, sync, or clean up off the request path.

Filtering by topic early keeps a busy store’s noise out of your handler. A HookWatch endpoint’s event-type allowlist can drop topics you do not act on before they reach your service.

Verifying authenticity

Shopify signs each delivery with the X-Shopify-Hmac-Sha256 header: the base64-encoded HMAC-SHA256 of the raw request body, keyed by your app’s shared secret.

X-Shopify-Hmac-Sha256: kR9vC1p6Vf8nQ2mX0aZ7bY4uT3sW6eH8jL5oP1dG9c=

To verify, compute HMAC-SHA256(shared_secret, raw_body), base64-encode the digest, and compare it to the header in constant time. Note the encoding: Shopify uses base64, not the hex encoding Stripe and GitHub use — a verifier written for one provider will fail on the other if the encoding is not switched. And, as always, hash the raw bytes, not a re-serialised body. The general treatment is in how to verify webhook signatures.

HookWatch’s custom inbound signature scheme covers this directly: header name X-Shopify-Hmac-Sha256, algorithm SHA-256, encoding base64, no prefix. With that configured, deliveries with a bad HMAC are rejected before your handler runs.

Common failure scenarios

  • Base64/hex encoding mismatch — reusing a hex verifier from another provider rejects every valid Shopify delivery.
  • Signature failures from a parsed body — a JSON middleware ahead of your verifier changes the bytes and breaks the HMAC.
  • Subscriptions removed after sustained failures — Shopify may remove a webhook subscription after an endpoint fails repeatedly over a period; the deliveries then simply stop. Phrase your monitoring around detecting this and re-registering, and consult current Shopify docs for the exact thresholds, which change.
  • Missed app/uninstalled — if this fails and is not recovered, you keep state and billing for a store that is gone.
  • Duplicate fulfilment — a retried delivery that already succeeded fulfils an order twice without dedupe.

Testing workflow

Shopify offers ways to send a test delivery for a topic from the admin, and app developers can trigger events by performing real actions in a development store — placing a test order to fire orders/create, for example. Because the available test tooling and its location move around, check Shopify’s current docs for what is available to you.

For local development, expose your handler with a tunnel and register a webhook (or a development-store webhook) against it so real Shopify deliveries reach your machine over public HTTPS. See testing webhooks locally; hookwatch tunnel forwards captured deliveries to a local process so Shopify can reach your laptop through a HookWatch endpoint.

Retry behaviour

Shopify retries failed deliveries for a limited window and then — unlike most providers — may remove the subscription entirely if failures persist. The exact retry schedule and removal threshold change, so rely on the shape of the behaviour and confirm current numbers in the docs. For your handler this means:

  • A 2xx is the only success signal; anything else counts as a failed attempt.
  • Persistent failure is not merely delayed delivery — it can end delivery. Alert on runs of failures rather than treating them as transient.
  • Retries and re-registration produce duplicates, so plan for at-least-once delivery.

The broader picture is in designing retry behaviour.

Idempotency

Dedupe on X-Shopify-Webhook-Id, which identifies a delivery and repeats across retries of that delivery. Record processed ids and make the effect idempotent — fulfilling one order twice, or double-charging on a duplicate orders/paid, is exactly the failure dedupe prevents. Because a store can resend and Shopify can retry, treat every topic as at-least-once. A duplicate you have already handled should still return 2xx. Patterns and storage choices are in idempotent webhook processing.

Debugging checklist

  1. Confirm the topic is still subscribed for the store and has not been removed after failures.
  2. Check the delivery’s recorded response — status, or a timeout — for the failed attempt.
  3. Confirm the handler reads the raw body before any parser runs.
  4. Verify the HMAC recomputes and is compared as base64, not hex.
  5. Confirm the shared secret matches the app that created the webhook.
  6. Route on X-Shopify-Topic and X-Shopify-Shop-Domain, not payload guesses.
  7. Ensure the handler returns 2xx fast, with fulfilment deferred.
  8. Match logs to the delivery by X-Shopify-Webhook-Id.
  9. Confirm duplicate deliveries are deduped and still answered 2xx.

How HookWatch helps

Point a HookWatch endpoint at Shopify and every delivery is recorded whole — the X-Shopify-Topic, X-Shopify-Hmac-Sha256, and X-Shopify-Webhook-Id headers, the body, your response, and each attempt — so a failed orders/paid still shows the exact order payload and your handler’s error when you look later. When a run of failures threatens your subscription, the captured deliveries let you replay the exact payloads (individually or in bulk) once you have fixed the handler, rather than losing the orders. Pause an endpoint during a deploy and resume it, filter to the topics you act on, and let repeated failures group into an incident so a Shopify problem is one alert with the evidence attached, not a silent gap in your orders.

Get started

See what happened to every webhook.

HookWatch keeps the request, the response, and every attempt for each delivery — so the debugging, retry, and replay steps in this article are a matter of reading, not reconstructing.