Test Shopify webhooks (and verify the HMAC)

Shopify signs every webhook with HMAC-SHA256(secret, raw body), base64-encoded, in the X-Shopify-Hmac-Sha256 header. A classic integration bug is verifying against a hex digest, or against re-serialized JSON — both look right and both fail. Easiest way to get it right: capture a real delivery first.

1. Get a capture URL

$ curl https://catchhook.catchhook.workers.dev/new

2. Register it as a webhook

Admin → Settings → Notifications → Webhooks → Create webhook (or via the Admin API / your app's webhook subscription). Pick an event like Order creation and paste your https://catchhook.catchhook.workers.dev/h/… URL. From the same screen Shopify lets you send a test notification — do that.

Open your bin dashboard: the capture shows the full JSON payload plus headers like X-Shopify-Topic, X-Shopify-Shop-Domain, X-Shopify-Webhook-Id and X-Shopify-Hmac-Sha256.

3. Verify the signature

In the bin's settings, choose the Generic HMAC-SHA256 signature scheme, set the header name to x-shopify-hmac-sha256, and paste your secret — the webhook signing secret shown in the notifications settings, or your app's API secret key for app-subscription webhooks. Every capture now gets a ✓ sig / ✗ sig badge; CatchHook accepts hex or base64 digests, so Shopify's base64 header verifies as-is.

Reference implementation for your handler (Node):

import crypto from 'node:crypto';
const digest = crypto.createHmac('sha256', secret)
  .update(rawBody).digest('base64');
const ok = crypto.timingSafeEqual(Buffer.from(digest),
  Buffer.from(req.headers['x-shopify-hmac-sha256']));

Gotcha: HMAC the raw request bytes. If your framework parses JSON before you can read the body (Express's express.json() without a verify callback), the re-stringified body won't match. And it's digest('base64'), not ('hex').

4. Build the handler against real payloads

Use copy as code on any capture to reproduce it against your local handler with the original headers and body, or Replay it to your real endpoint once deployed. The replayed body is byte-identical, so your HMAC check still verifies.

No signup needed. Or from your terminal: curl https://catchhook.catchhook.workers.dev/new

← All guides · Docs · CatchHook vs webhook.site