Shopify Webhook: Example Payload, Headers and Signature

What Shopify posts when a store event fires — the topic and shop headers, the resource JSON, and a base64 HMAC that trips up people expecting hex.

No webhook URL yet. Creating one needs no account.

Request details
Requests sent to your webhook URL appear here: method, headers and the body exactly as it arrived.

The button posts the example below to your webhook URL from your browser. It is not sent by Shopify, it carries your browser's User-Agent, and its signature is a placeholder that will not verify. To see a real delivery, follow the steps further down.

Last updated . How this page is checked.

How the webhook URL works. It is a public address that records whatever is sent to it and answers 200. An inbox keeps the 50 most recent requests, accepts bodies up to 256 KB, and is deleted 24 hours after it was created — or sooner if you press Delete URL or our server restarts, because requests are held in memory and never written to disk.

Anyone who has the URL can read what it received. Use sandbox and test-mode accounts, do not route production traffic or customer data through it, and delete it when you are done. Privacy details.

What is a Shopify webhook?

A Shopify webhook is an HTTP POST sent to an address subscribed to a topic such as orders/create, products/update or app/uninstalled. The body is the resource itself — an order, a product, a customer — in the shape of the API version named in X-Shopify-API-Version. There is no envelope: the topic and the shop it came from are only in the headers.

Subscriptions belong to an app. They can be declared in the app's configuration file, created through the Admin API, or — for a single store — added by a merchant under Settings → Notifications → Webhooks.

What a Shopify webhook request looks like

The request line and headers as they reach your server, followed by the body. Ids, tokens and the signature are placeholders.

POST /your/webhook/path HTTP/1.1
Host: your-server.example
X-Shopify-Topic: orders/create
X-Shopify-Hmac-Sha256: XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=
X-Shopify-Shop-Domain: example-store.myshopify.com
X-Shopify-API-Version: 2025-10
X-Shopify-Webhook-Id: b54557e4-bdd9-4b37-8a5f-bf7d70bcd043
X-Shopify-Event-Id: 98880550-7158-44d4-b7cd-2c97c8a091b5
X-Shopify-Triggered-At: 2026-01-01T12:00:00.123456789Z
{
  "id": 820982911946154500,
  "admin_graphql_api_id": "gid://shopify/Order/820982911946154508",
  "name": "#1001",
  "order_number": 1001,
  "created_at": "2026-01-01T07:00:00-05:00",
  "currency": "USD",
  "total_price": "59.90",
  "subtotal_price": "49.90",
  "total_tax": "0.00",
  "financial_status": "paid",
  "fulfillment_status": null,
  "test": true,
  "email": "jon@example.com",
  "customer": {
    "id": 115310627314723950,
    "email": "jon@example.com",
    "first_name": "Jon",
    "last_name": "Doe"
  },
  "line_items": [
    {
      "id": 866550311766439000,
      "title": "Short Sleeve T-Shirt",
      "quantity": 1,
      "price": "49.90",
      "sku": "TS-BLK-M",
      "variant_id": 808950810
    }
  ],
  "shipping_lines": [
    {
      "title": "Standard",
      "price": "10.00"
    }
  ]
}

Prices are decimal strings. Ids are 64-bit integers that exceed what a JavaScript number can hold exactly — parse them as strings or use admin_graphql_api_id. test: true marks an order placed through a test gateway.

Headers Shopify sends

HeaderExampleWhat it is
X-Shopify-Topicorders/createThe event. The body does not repeat it.
X-Shopify-Hmac-Sha256XWmrwMey6OsLMeiZKwP4FppHH3cmAiiJJAweH5Jo4bM=HMAC-SHA256 of the raw body, base64-encoded.
X-Shopify-Shop-Domainexample-store.myshopify.comWhich store sent it — the key for looking up the right tenant.
X-Shopify-API-Version2025-10API version the payload was serialised with.
X-Shopify-Webhook-Idb54557e4-bdd9-4b37-8a5f-bf7d70bcd043Unique id of this delivery, constant across retries.
X-Shopify-Event-Id98880550-7158-44d4-b7cd-2c97c8a091b5Id of the underlying event. Several subscriptions can receive the same event.
X-Shopify-Triggered-At2026-01-01T12:00:00.123456789ZWhen the event happened, which is not when it was delivered.

Header names are case-insensitive. Your framework may show them lower-cased, and HTTP/2 always does.

How Shopify signs a webhook

Shopify computes an HMAC-SHA256 of the raw request body using the app's client secret (called the API secret key in older screens) and sends the digest base64-encoded in X-Shopify-Hmac-Sha256. Webhooks created by a merchant in the store admin are signed with a key shown on that notifications page instead.

Compute it over the raw request body exactly as received — before any JSON parsing — and compare with a constant-time function. Compare against the base64 form: hex-encoding your digest is the single most common reason this check fails.

const crypto = require('crypto');

// rawBody: Buffer of the body exactly as received
function verifyShopify(rawBody, header, clientSecret) {
  const expected = crypto.createHmac('sha256', clientSecret)
    .update(rawBody).digest('base64');
  const a = Buffer.from(header || '');
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

The five-second rule and retries

Shopify waits about five seconds for a response, per its documentation at the time of writing, and treats anything other than a 2xx in that window as a failure. That is short enough that doing real work inline — calling another API, writing several rows — will eventually time out under load. Store the payload, return 200, process afterwards.

Failed deliveries are retried for a limited period measured in hours, with growing gaps. A subscription created through the API that keeps failing through that period is removed, and the app has to re-register it — so a long outage does not just lose events, it can silently end the subscription. Reconcile with a periodic API fetch for anything that matters.

Catch a real Shopify webhook

  1. Press Create webhook URL at the top of this page and copy the address.
  2. In a development store's admin, open Settings → Notifications → Webhooks and choose Create webhook.
  3. Pick an event such as Order creation, keep the format as JSON, paste the address and save.
  4. Use Send test notification on that row, or place a test order.
  5. Read the headers and the order JSON in the inbox above.

App developers can do the same from a terminal with the Shopify CLI's shopify app webhook trigger command, pointing --address at the URL.

To run a caught request against your own code, select it and press Open in API Tester: the method, headers and body are loaded into the request console, where you enter your server's public URL and send it. A body you edit will no longer match its signature — that is the signature doing its job.

Things that catch people out

  • Base64, not hex. Worth repeating, because the failure looks exactly like a wrong secret.
  • Duplicates are normal. De-duplicate on X-Shopify-Webhook-Id (or the event id), and expect orders/updated to fire many times for one checkout.
  • Mandatory privacy webhooks. Public apps must handle customers/data_request, customers/redact and shop/redact, and must reject an invalid HMAC with 401.

api-tester.com is independent and is not affiliated with Shopify. Providers change header names, limits and retry schedules; treat the official Shopify documentation as the source of truth.

Related examples and tools

Other webhooks signed with an HMAC of the body

References

These links leave api-tester.com, which is independent of the companies and projects they point to.

Shopify webhook FAQ

What does a Shopify webhook payload look like?

The JSON of the resource itself — an order for orders/create, a product for products/update — with no wrapper. The topic, shop domain and API version are sent as headers.

How do I verify a Shopify webhook?

Compute an HMAC-SHA256 of the raw body with the app's client secret, base64-encode the digest and compare it to the X-Shopify-Hmac-Sha256 header using a constant-time comparison.

How fast must I respond to a Shopify webhook?

Within roughly five seconds according to Shopify's documentation at the time of writing. Acknowledge immediately and process the payload asynchronously.

What happens if my Shopify webhook endpoint is down?

Shopify retries for a limited period. If an API-created subscription keeps failing, Shopify removes it and the app must subscribe again, so reconcile important data with the API.

Which header tells me which store sent the webhook?

X-Shopify-Shop-Domain carries the store's myshopify.com domain, and X-Shopify-Topic names the event.