Testing Shopify Webhooks with HookDeploy
Shopify Admin API webhooks, HMAC verification, mandatory webhooks, rate limits, and GDPR compliance webhooks.
HookDeploy Team · May 22, 2026
Shopify webhooks notify your app about store events — orders, products, customers, inventory, and compliance requests. This guide covers setup, verification, and the Shopify-specific quirks that catch developers off guard.
Admin API webhooks vs Partner webhooks
| Type | Configured in | Scope |
|---|---|---|
| Admin API (custom app) | Shopify Admin → Settings → Notifications | Single store |
| App webhooks (Partner Dashboard) | partners.shopify.com → App setup | All stores that install your app |
| GraphQL webhookSubscriptionCreate | API call | Programmatic, per shop |
For development, configure webhooks in the Shopify Admin of a development store. For production apps, register webhooks via the Partner Dashboard or GraphQL API.
Set up HookDeploy with a development store
- Create a Shopify development store
- Create a HookDeploy endpoint
- In Shopify Admin: Settings → Notifications → Webhooks → Create webhook
- Event: start with
Order creationorProduct creation - Format: JSON
- URL:
https://hookdeploy.dev/h/{slug} - Webhook API version: latest stable
- Save
Shopify sends a test delivery immediately. Verify it appears in HookDeploy.
HMAC verification
Shopify signs every webhook with HMAC-SHA256. The signature is in X-Shopify-Hmac-Sha256, base64-encoded:
const crypto = require('crypto');
function verifyShopifyWebhook(rawBody, hmacHeader, secret) {
const hash = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(hash),
Buffer.from(hmacHeader)
);
}
// Express handler:
const hmac = request.headers['x-shopify-hmac-sha256'];
if (!verifyShopifyWebhook(request.rawBody, hmac, process.env.SHOPIFY_WEBHOOK_SECRET)) {
return response.status(401).send('Invalid HMAC');
}
The secret is your app’s API secret key (Partner Dashboard → App setup → Client credentials), not the API access token.
Mandatory webhooks for public apps
Public Shopify apps must handle these compliance webhooks:
| Topic | Purpose |
|---|---|
customers/data_request | Customer requests their data (GDPR) |
customers/redact | Customer requests data deletion |
shop/redact | Store uninstalled your app — delete shop data |
Shopify reviews your app for compliance webhook handling before approval. Register these in the Partner Dashboard under App setup → Compliance webhooks.
Test with HookDeploy first — capture the payload structure, then implement handlers.
Common Shopify webhook topics
| Topic | Fires when |
|---|---|
orders/create | New order placed |
orders/paid | Order payment confirmed |
orders/fulfilled | Order fully fulfilled |
products/create | New product added |
products/update | Product modified |
inventory_levels/update | Inventory quantity changes |
app/uninstalled | Merchant uninstalls your app |
Rate limits
Shopify limits webhook delivery rate per shop. Excessive failures can cause Shopify to remove your webhook subscription after 19 failed attempts over 48 hours.
Shopify expects a response within 5 seconds — shorter than Stripe or GitHub. Return 200 immediately:
app.post('/webhooks/shopify', (req, res) => {
if (!verifyShopifyWebhook(req.rawBody, req.headers['x-shopify-hmac-sha256'], secret)) {
return res.status(401).send('Unauthorized');
}
res.status(200).send('OK');
processShopifyWebhook(req.body).catch(console.error);
});
GDPR webhooks in detail
customers/data_request — respond within 30 days with customer data you store. Payload includes customer ID and email.
customers/redact — delete customer data within 30 days. Payload includes customer ID.
shop/redact — sent 48 hours after app uninstall. Delete all data for that shop.
These are legal requirements for EU customers. Implement handlers even if you store no customer data (respond with 200 and log the request).
Test with curl
# Compute HMAC for testing (Node.js one-liner):
# node -e "const c=require('crypto');const b='{\"test\":true}';console.log(c.createHmac('sha256','YOUR_SECRET').update(b).digest('base64'))"
curl -X POST "https://hookdeploy.dev/h/YOUR_SLUG" \
-H "Content-Type: application/json" \
-H "X-Shopify-Topic: orders/create" \
-H "X-Shopify-Shop-Domain: your-store.myshopify.com" \
-H "X-Shopify-Hmac-Sha256: COMPUTED_HMAC" \
-d '{"id": 12345, "total_price": "29.99", "currency": "USD"}'
Debug delivery failures
In Shopify Admin → Settings → Notifications → Webhooks, each webhook shows recent delivery status.
Common issues:
- 401 from your handler — HMAC verification failing (wrong secret or parsed body)
- 404 — HookDeploy slug wrong or endpoint deleted
- Timeout — handler took > 5 seconds before returning 200
- Removed webhook — 19 consecutive failures — re-create the webhook subscription
Use HookDeploy to capture the exact payload Shopify sends before building your handler.
Related guides
Related guides
- Webhook security best practices
Signature verification for Stripe, GitHub, and Shopify. HMAC-SHA256, replay attacks, IP allowlisting, HTTPS, and secret rotation.
- Webhook debugging guide
Systematic approach to debugging webhook issues — delivery logs, signatures, payloads, curl testing, and common HTTP error codes.
- Testing Stripe webhooks with HookDeploy
Complete guide to capturing, inspecting, and debugging Stripe webhook events using HookDeploy, the Stripe CLI, and signature verification.