Developer reference

Shopify Webhooks for Variant Changes

Polling Shopify's Admin API every 5 minutes to detect variant changes is the wrong pattern in 2026 — it burns rate-limit budget, misses transient states, and adds latency. Webhooks push change events to your endpoint within seconds. Here is the complete developer reference: which webhook topics matter for personalization apps, how to verify HMAC signatures, how to handle retry storms, and how to fan out to EventBridge / Kafka for downstream systems.

Last updated: August 23, 2026~12 min readBy the Print It My Way team

Which webhook topics matter for variants

Shopify emits ~40 webhook topics. For personalization apps tracking variant changes, only 6 matter in practice: products/create, products/update, products/delete, inventory_levels/update, inventory_items/update, and bulk_operations/finish. There is no separate variants/update topic — variant changes come through products/update because a variant edit changes the parent product's version.

Subscribing to webhooks

mutation WebhookSubscribe {
  webhookSubscriptionCreate(
    topic: PRODUCTS_UPDATE,
    webhookSubscription: {
      callbackUrl: "https://your-app.com/webhooks/products-update",
      format: JSON
    }
  ) {
    webhookSubscription { id topic callbackUrl }
    userErrors { field message }
  }
}

Subscribe once per topic per shop during app installation (OAuth callback). Verify existing subscriptions via webhookSubscriptions query and avoid duplicate subscribes.

HMAC verification (mandatory)

Every webhook request includes an X-Shopify-Hmac-Sha256 header. Verify it before processing — Shopify's webhook URLs are guessable, and unverified endpoints are attack surface.

// Node.js Express example
const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const hmac = req.headers['x-shopify-hmac-sha256'];
  const body = req.rawBody; // must be the raw buffer, NOT parsed JSON
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('base64');
  return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(expected));
}

app.post('/webhooks/products-update', (req, res) => {
  if (!verifyWebhook(req, process.env.SHOPIFY_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  // Process webhook
  res.status(200).send('OK');
});
Body must be the raw buffer. Express's body-parser middleware parses JSON by default, which mutates the body and breaks HMAC. Use express.raw({type:'application/json'}) for webhook routes or bypass parsing entirely.

Retry semantics

Shopify retries failed webhooks for 48 hours with exponential backoff (up to 19 retries). Your endpoint must return a 2xx status within 5 seconds; anything else counts as failure and triggers retry.

Diffing variant changes

The products/update webhook fires on every product edit, including title, description, media, tags, options, and variant changes. You almost never care about all of them. Diff the incoming payload against your stored snapshot to detect what actually changed:

function diffVariants(oldProduct, newProduct) {
  const oldById = Object.fromEntries(oldProduct.variants.map(v => [v.id, v]));
  const newById = Object.fromEntries(newProduct.variants.map(v => [v.id, v]));
  const changes = { created: [], updated: [], deleted: [] };

  for (const id in newById) {
    if (!oldById[id]) changes.created.push(newById[id]);
    else if (JSON.stringify(oldById[id]) !== JSON.stringify(newById[id])) {
      changes.updated.push({ old: oldById[id], new: newById[id] });
    }
  }
  for (const id in oldById) {
    if (!newById[id]) changes.deleted.push(oldById[id]);
  }
  return changes;
}

EventBridge / Kafka fan-out

At scale (100+ shops), route Shopify webhooks into an event bus (AWS EventBridge, Google Pub/Sub, Kafka) so multiple downstream services (POD partner sync, ERP mirror, analytics warehouse, cache invalidation) can consume without coupling to your webhook receiver:

Shopify → Your webhook endpoint → EventBridge topic "shopify.products.update"
    ↓                                        ↓
  (verify HMAC,                       Multiple subscribers:
   queue for retry)                   - POD partner sync
                                      - ERP sync
                                      - Cache invalidation
                                      - Search index update

EventBridge webhooks (native Shopify integration)

Shopify supports EventBridge as a native webhook destination (no HTTP endpoint required). Configure via webhookSubscriptionCreate with eventBridgeWebhookSubscription. Shopify pushes directly into your EventBridge partner event source. Simplifies infrastructure and eliminates the HMAC-verification step (EventBridge handles authentication).

Frequently asked questions

Is there a Shopify webhook for variant changes?

Not directly — variant changes come through the products/update webhook, which fires whenever any part of a product (including its variants) is edited. Diff the incoming payload against your stored version to detect specific variant changes.

How do I verify Shopify webhooks are authentic?

Verify the X-Shopify-Hmac-Sha256 header. Compute HMAC-SHA256 of the raw request body using your app's webhook secret, base64-encode it, and compare to the header value using a timing-safe comparison. Reject any request that fails.

How does Shopify retry failed webhooks?

Retries for 48 hours with exponential backoff, up to 19 attempts. Your endpoint must return 2xx within 5 seconds to count as success. Queue payloads and process asynchronously — never handle webhooks synchronously.

Are Shopify webhooks guaranteed in order?

No — webhooks are eventually consistent and may arrive out of order. Include the payload's updated_at field in your dedup + apply logic to handle out-of-order delivery.

How do I dedupe Shopify webhook retries?

Use X-Shopify-Webhook-Id as the dedup key. Retries carry the same ID. Store processed IDs in Redis with a 48-hour TTL and skip already-processed IDs.

Can I send Shopify webhooks directly to AWS EventBridge?

Yes — Shopify has native EventBridge integration. Subscribe via webhookSubscriptionCreate with eventBridgeWebhookSubscription. Eliminates the need for a public HTTP endpoint and offloads HMAC verification.

What webhook topics does a personalization app need?

products/create, products/update, products/delete, inventory_levels/update, orders/create, orders/updated, bulk_operations/finish. Optionally refunds/create for reversal logic and customers/data_request for GDPR compliance.

How do I handle webhook floods when a merchant bulk-updates 1000 variants?

Rate-limit your worker (process max N/sec per shop), deduplicate incoming webhooks by product ID (only process the latest for a given product ID per minute), and use a priority queue that batches many product updates from the same shop into one downstream sync.

Related reading

Try it free on Shopify

Print It My Way's permanent Free plan handles webhook fan-out to POD partners, ERP sync, and cache invalidation out of the box — no custom app development needed.

Install Print It My Way