Shopify only delivers webhooks to public HTTPS URLs. That's a problem the moment you want to build an orders/create handler on your laptop. You end up hardcoding fixtures, deploying half-finished code to staging just to see a real payload, or wrestling with a tunnel that mangles the request body and breaks X-Shopify-Hmac-Sha256 verification.
Relayers gives you a stable public ingest URL, forwards the untouched request to localhost:3000, and lets you filter and transform events with JQ before they hit your code. This walkthrough gets a real Shopify webhook landing on your dev server in a few minutes.
What you'll build
Shopify sends to a public Relayers URL. Relayers routes the event down a WebSocket tunnel to the relayers CLI running on your machine, which forwards it to your local app.
Prerequisites
- A Shopify store (or a custom/public app) where you can manage webhooks.
- A local server listening on a port (this guide uses
3000). - The Relayers desktop app or CLI. Sign up free at relayers.app and download the app.
1Create an endpoint in Relayers
Open the desktop app and go to Endpoints → Create. Name it something like shopify-dev. Relayers gives you a public ingest URL shaped like this:
https://api.relayers.app/v1/webhooks/wep_a1b2c3d4e5
Click Copy URL. You'll paste it into Shopify next. While you're here, hit Send test to confirm the endpoint is live before touching Shopify.
2Register the webhook in Shopify
You have two options depending on whether you're configuring a store or an app.
Store admin (quickest): go to Settings → Notifications → Webhooks, click Create webhook, then:
- Event / Topic:
orders/create - Format: JSON
- URL: paste your
https://api.relayers.app/v1/webhooks/wep_...URL
App config (for custom/public apps): declare the subscription in shopify.app.toml, or register it through the Admin API:
[[webhooks.subscriptions]]
topics = ["orders/create"]
uri = "https://api.relayers.app/v1/webhooks/wep_a1b2c3d4e5"
Common topics you'll wire up: orders/create, orders/paid, products/update, and app/uninstalled.
Every delivery Shopify sends carries X-Shopify-Hmac-Sha256 (a base64 HMAC-SHA256 of the raw body, signed with your app's client secret), plus X-Shopify-Topic and X-Shopify-Shop-Domain. Relayers forwards all of these untouched, which matters in step 5.
3Forward events to localhost
Authenticate the CLI, then start listening:
relayers login
relayers listen --forward localhost:3000
relayers listen opens the tunnel. Now trigger a real event: create a test order in your store (or click Send test in the desktop app). Within a second the payload should hit localhost:3000, with the same body and headers Shopify sent.
Open the Events view in the desktop app to inspect every delivery. The payload inspector shows the raw body and headers, and you can retry any event without re-triggering it in Shopify.
4Route and filter with JQ
You rarely want every order in your local handler. In the Rules tab for your endpoint, add a JQ filter so only paid orders above a threshold get forwarded:
.financial_status == "paid" and (.total_price | tonumber) > 50
Set the rule's destination to the tunnel (localhost:3000). Rules evaluate top to bottom and first match wins, so order them from most specific to most general and drag to reorder. Anything that doesn't match is dropped instead of waking up your dev server.
5Transform the payload
Shopify order payloads are large. Reshape them to exactly what your handler needs in the Transformations tab. Pick jq and map the fields you care about:
{
order: .id,
total: .total_price,
email: .email,
items: (.line_items | length)
}
The test playground lets you paste a sample order and see the output before you save. Transformations also support JSONata, JavaScript, and Go templates if JQ isn't your thing.
Signatures still verify locally
This is the part most tunnels get wrong. Shopify's HMAC is computed over the raw request body. If anything re-serializes or reformats the JSON in transit, the bytes change and X-Shopify-Hmac-Sha256 no longer matches.
Relayers forwards the original body and headers byte-for-byte. Your local verification code works unchanged:
const crypto = require("crypto");
function verifyShopify(rawBody, hmacHeader, secret) {
const digest = crypto
.createHmac("sha256", secret)
.update(rawBody, "utf8")
.digest("base64");
return crypto.timingSafeEqual(
Buffer.from(digest),
Buffer.from(hmacHeader)
);
}
Just make sure your framework hands you the raw body (not a parsed object) before you compute the digest.
Going to production
When you deploy, point Shopify at your real backend URL and keep Relayers in front as your routing and observability layer. The rules and transformations you built locally carry over. Use Export to save your endpoint, rules, and transformations as a JSON bundle, and Import it into another workspace or environment so dev and prod stay in sync.
For production destinations, a rule's target can be a public URL instead of the tunnel, so Relayers can fan the same Shopify event out to multiple services.
Troubleshooting
- HMAC verification fails: the body was altered somewhere. Confirm your server reads the raw body before parsing, and that no middleware rewrote the JSON. Relayers preserves the body, so the usual culprit is a body parser running before your verification.
- No events arriving: check the topic in Shopify matches what you expect, and confirm
relayers listenis connected. Use the Events view to see whether Relayers received the delivery at all — if it did, the problem is downstream of the tunnel. - Wrong events reaching your handler: tighten the JQ filter. Remember first match wins, so a broad rule higher in the list can shadow a more specific one below it.
- Deliveries timing out: Shopify expects a fast response (roughly a 5-second window) and retries on failure. Keep your handler's synchronous work minimal — acknowledge quickly and process asynchronously. Use retry in the Events view to replay anything that failed while you debug.
Wrap-up
You now have real Shopify webhooks landing on localhost:3000, filtered to just the orders you care about, reshaped to your schema, with HMAC verification intact. No staging deploys, no fixture guessing.
Start free at relayers.app and download the desktop app to get your first endpoint running.