Receiving Shopify Webhooks: Configuration & Security Guide

Updated October 6, 202512 min read

Shopify webhooks let your app react instantly when merchants create orders, update products, or change store settings. This guide blends official documentation with real-world practices so you can choose the right delivery method, automate subscription management, harden security, and operate webhook workloads at scale.

How Shopify Webhooks Work

Shopify raises a webhook whenever a subscribed event fires, signs the JSON payload using your app secret, and delivers it to the endpoint (or event bus) you configured. Return 200 OK within five seconds—Shopify retries failed deliveries up to eight times across several hours and may delete the subscription after repeated failures.

  • Topics define which events trigger delivery, such as orders/create or products/update.
  • Delivery channels include HTTPS endpoints, Google Pub/Sub topics, and Amazon EventBridge.
  • Idempotency is required—Shopify can deliver duplicates and events are not strictly ordered.

Key Shopify Webhook Headers

Inspect headers to authenticate requests, deduplicate events, and instrument observability.

HeaderPurpose
X-Shopify-TopicEvent topic (for routing, e.g., orders/create)
X-Shopify-Hmac-Sha256HMAC signature generated with your app secret
X-Shopify-Shop-DomainShop domain that triggered the webhook
X-Shopify-Webhook-IdUnique delivery identifier for auditing and retries
X-Shopify-Event-IdUnique event identifier—store it to prevent duplicate processing
X-Shopify-Triggered-AtTimestamp for ordering out-of-sequence deliveries

Choose a Delivery Method

Start by selecting the channel that matches your infrastructure. Most apps use an HTTPS endpoint because it works everywhere, while Pub/Sub and EventBridge integrate natively with Google Cloud and AWS event pipelines.

MethodBest ForHighlights
HTTPS endpointBackend services or serverless functionsSimple setup, signature verification, automatic retries
Google Pub/SubEvent-driven Google Cloud workloadsFan-out processing to Cloud Functions, Dataflow, BigQuery
Amazon EventBridgeAWS-native integrations and Step FunctionsRoute with EventBridge rules—no web server to manage

Register Webhook Subscriptions

Shopify offers two primary approaches. Use the app configuration file when every install needs the same set of topics. Reach for the GraphQL Admin API when merchants should opt in to specific events or when you want to apply filters programmatically.

Prerequisites

  • Admin API access token with the write_* scopes required by each topic.
  • Publicly reachable HTTPS endpoint with a valid SSL certificate.
  • Configured api_version so payload schemas align with your app release cadence.

App configuration file

Define subscriptions declaratively alongside your app source. Shopify provisions them automatically during installation and reconciles diffs whenever you deploy updates.

[webhooks]
api_version = "2023-04"

[[webhooks.subscriptions]]
topics = ["orders/create", "products/update"]
uri = "https://your-backend.com/webhooks"
  • topics: select events from Shopify's webhook topic catalog.
  • uri: use HTTPS in production; for local testing tunnel traffic with Hooklistener or ngrok.
  • Keep this file under version control so updates roll out consistently.

GraphQL Admin API

Use a webhookSubscriptionCreate mutation when subscriptions depend on merchant settings or you need to inject filters dynamically.

mutation webhookSubscriptionCreate {
  webhookSubscriptionCreate(
    topic: ORDERS_CREATE
    webhookSubscription: {
      format: JSON
      callbackUrl: "https://your-backend.com/webhooks"
      includeFields: ["id", "email", "total_price"]
    }
  ) {
    webhookSubscription {
      id
      topic
      endpoint {
        __typename
        ... on WebhookHttpEndpoint {
          callbackUrl
        }
      }
    }
    userErrors {
      field
      message
    }
  }
}
  • Check userErrors to catch missing scopes or invalid filters.
  • Persist the subscription id so you can delete or rotate it later.
  • Apply filter or includeFields to manage payload volume per merchant.

Implement a Verified Webhook Endpoint

Shopify expects endpoints to validate authenticity, respond quickly, and queue heavy work. Capture the raw body before parsing it so HMAC verification succeeds.

import express from "express";
import crypto from "crypto";

const app = express();
const secret = process.env.SHOPIFY_WEBHOOK_SECRET ?? "";

app.post(
  "/webhooks",
  express.text({ type: "*/*" }),
  async (req, res) => {
    const hmacHeader = req.get("X-Shopify-Hmac-Sha256") ?? "";
    const digest = crypto
      .createHmac("sha256", secret)
      .update(req.body, "utf8")
      .digest("base64");

    if (!crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(hmacHeader))) {
      return res.status(401).send("Unauthorized");
    }

    res.status(200).send("OK");

    const topic = req.get("X-Shopify-Topic");
    const shop = req.get("X-Shopify-Shop-Domain");
    const payload = JSON.parse(req.body);
    await enqueueWebhook({ topic, shop, payload });
  },
);

async function enqueueWebhook(event: {
  topic: string | null;
  shop: string | null;
  payload: unknown;
}) {
  // Push onto a queue (BullMQ, SQS, Cloud Tasks, etc.) for async processing
}

app.listen(3000, () => console.log("Webhook server listening on port 3000"));
// Using @shopify/shopify-app-express to validate requests
import { shopifyApp } from "@shopify/shopify-app-express";

const shopify = shopifyApp({
  api: {
    apiKey: process.env.SHOPIFY_API_KEY!,
    apiSecretKey: process.env.SHOPIFY_API_SECRET!,
  },
});

app.post(
  "/webhooks",
  express.text({ type: "*/*" }),
  async (req, res) => {
    const { valid, topic, domain } = await shopify.webhooks.validate({
      rawBody: req.body,
      rawRequest: req,
      rawResponse: res,
    });

    if (!valid) {
      return res.status(400).send("Bad Request");
    }

    res.status(200).send("OK");
    await enqueueWebhook({ topic, shop: domain, payload: JSON.parse(req.body) });
  },
);

Filter Events to Reduce Noise

Shopify lets you filter subscriptions so that only specific objects trigger delivery. Filters use the same syntax as Shopify search, giving you precise control over payload volume.

[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "https://your-backend-service.com/webhooks"
filter = "status:active AND variants.price:>=100"
  • Filters run before delivery, lowering processing and storage costs.
  • Apply filters in configuration files and GraphQL mutations.
  • Document applied filters so downstream services know which events are omitted.

Trim Payloads to Essential Fields

Large Shopify objects can exceed 100 KB. Payload transformations let you request only the fields your backend needs, speeding up processing and storage.

  • Use includeFields in GraphQL mutations to deliver a trimmed schema.
  • Store payload documentation alongside your code to avoid brittle assumptions.
  • Combine payload shaping with filters for the leanest possible webhook traffic.

Secure Incoming Shopify Requests

Every webhook must be verified to prevent spoofing. Shopify signs the request body using your app secret and places the digest in X-Shopify-Hmac-Sha256. Recreate the signature with the same secret before trusting the payload.

import { createHmac, timingSafeEqual } from "crypto";

export function verifyShopifyWebhook(rawBody: Buffer, hmacHeader: string, secret: string) {
  const digest = createHmac("sha256", secret).update(rawBody).digest("base64");
  const valid = timingSafeEqual(Buffer.from(digest), Buffer.from(hmacHeader));

  if (!valid) {
    throw new Error("Invalid Shopify signature");
  }
}

export function handleDuplicate(eventId: string, store: Set<string>) {
  if (store.has(eventId)) {
    return false;
  }

  store.add(eventId);
  return true;
}

Security checklist

  • Capture the raw request body before any JSON parsing middleware runs.
  • Log X-Shopify-Webhook-Id and X-Shopify-Event-Id for replay detection.
  • Use X-Shopify-Triggered-At to order out-of-sequence deliveries.
  • Rotate webhook secrets if your app secret changes and update verification code simultaneously.

Performance & Reliability Tactics

Reliability

  • Respond within five seconds; enqueue heavy processing for background jobs.
  • Implement retry-aware queues (BullMQ, SQS, Cloud Tasks) to smooth bursts.
  • Run reconciliation cron jobs to compare webhook events with API state.

Performance

  • Enable HTTP keep-alive on outbound calls made during processing.
  • Use connection pools or serverless concurrency limits to handle spikes.
  • Instrument success and failure metrics per topic to spot regressions quickly.

Common Shopify Webhook Topics

TopicDescription
orders/createTriggered when a new order is created
orders/updatedOrder details have changed
orders/fulfilledFulfillment status updated to fulfilled
products/updateProduct information changed
customers/createNew customer account created
inventory_levels/updateInventory quantity changed for a location

Mandatory compliance topics

Public apps distributed through the Shopify App Store must subscribe to:

  • customers/data_request
  • customers/redact
  • shop/redact

Testing & Troubleshooting

  • Expose local endpoints with Hooklistener tunnels or ngrok: ngrok http 3000.
  • Trigger test webhooks via the Admin API or by simulating events in a development store.
  • Investigate missing webhooks by checking HTTPS availability, SSL validity, and subscription status.
  • Track signature failures to surface secret mismatches or body parsing issues.

Summary of Subscription Options

MethodSetup LocationPrimary Use CaseExample Topic
App configuration fileApp source or config repoStandardized events for every installproducts/create
GraphQL Admin APIApp backend at install timePer-shop customization or opt-in eventsorders/create
Google Pub/SubGoogle Cloud projectEvent pipelines and analytics workloadsapp/uninstalled
Amazon EventBridgeAWS accountServerless routing and Step Functionsorders/fulfilled

Build Faster with Hooklistener

Hooklistener captures Shopify webhooks in real-time, forwards them to your tunnels, and keeps an audit trail for replay. Ship production-ready integrations faster with tooling built for modern webhook teams.

Instant capture and replay of Shopify events
Signature verification test harness
Team-friendly history and diff tooling
Automatic retries with failure alerts

Further Reading