Open-Source Cold Email & Warmup Platform
Warmbly is the open-source platform for cold email: warm your mailboxes, send campaigns, and triage every reply. Run it on our cloud or self-host the whole thing.
Free Shopify store audit: score your store's SEO, speed, conversion (CRO) and AI visibility, with the top issues to fix and a free PDF report.
MarketingJavaScriptGitHub @EcomswiftLLCOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Verify, log, and replay Shopify webhooks locally: HMAC-SHA256 verification, X-Shopify-Webhook-Id dedupe, a zero-dependency dev server, and a replay CLI.
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Verify, log, and replay Shopify webhooks locally — a zero-dependency dev server plus a small CLI, so you can build and test a webhook handler without a live store, a tunnel, or waiting for Shopify to fire the event again.
We built this because every Shopify app we've shipped needed the same three things on day one: verify the X-Shopify-Hmac-Sha256 signature correctly (the raw-body gotcha below has bitten us more than once), dedupe retried deliveries, and replay a captured payload while iterating on a handler. This is that, extracted and made reusable.Read the full README ↗Verify, log, and replay Shopify webhooks locally — a zero-dependency dev server plus a small CLI, so you can build and test a webhook handler without a live store, a tunnel, or waiting for Shopify to fire the event again.
We built this because every Shopify app we've shipped needed the same three things on day one: verify the X-Shopify-Hmac-Sha256 signature correctly (the raw-body gotcha below has bitten us more than once), dedupe retried deliveries, and replay a captured payload while iterating on a handler. This is that, extracted and made reusable.
Free audit for your own store while you're here: audit.ecomswiftllc.com checks SEO, speed, CRO and AI-visibility in one pass.
const { createWebhookServer } = require('shopify-webhook-toolkit');
const server = createWebhookServer({
secret: process.env.SHOPIFY_WEBHOOK_SECRET,
logDir: './webhook-captures',
onWebhook(ctx) {
// ctx.verified, ctx.topic, ctx.shop, ctx.webhookId, ctx.body (parsed JSON)
console.log(`${ctx.topic} from ${ctx.shop}`, ctx.body);
},
});
server.listen(3000);
$ npx swt listen --secret shpss_xxx --log ./webhook-captures
shopify-webhook-toolkit listening on http://localhost:3000/webhooks
[swt] received topic=orders/create shop=example-shop.myshopify.com id=<webhook-id> verified=true
[swt] handled topic=orders/create shop=example-shop.myshopify.com id=<webhook-id> verified=true
Every request is answered fast (before your handler even runs) and logged to disk, so you can replay it later:
$ npx swt replay ./webhook-captures/captures/orders_create__<webhook-id>.json \
--url http://localhost:3000/webhooks --secret shpss_xxx
swt replay: 200 OK
verifyHmac(rawBody, hmacHeader, secret) — HMAC-SHA256 verification against the raw request body, using crypto.timingSafeEqual for the comparison (the pattern Shopify's own docs use), not a naive ===.createWebhookServer(options) — a dependency-free HTTP server for local development: verifies signatures, responds within milliseconds (Shopify times out and retries slow endpoints), then runs your handler.X-Shopify-Webhook-Id via IdempotencyStore, exported separately for reuse in your real server.swt replay — resend a captured or hand-written payload to any URL, with the signature recomputed for the target secret, so you can drive a handler with real-shaped data on demand.swt sign — print the HMAC signature for a payload file, plus a ready-to-run curl command, for manual testing.node:http, node:crypto, fetch). No framework required, though examples/express-middleware.js shows the Express integration if you use one.npm install shopify-webhook-toolkit
# or use the CLI without installing:
npx shopify-webhook-toolkit --help
Requires Node.js 18 or later (for the built-in fetch used by replay).
const { createWebhookServer, IdempotencyStore, verifyHmac } = require('shopify-webhook-toolkit');
verifyHmac(rawBody, hmacHeader, secret) → boolean. Use this inside your own framework's route if you're not using createWebhookServer directly — see examples/express-middleware.js.createWebhookServer({ secret, onWebhook, onInvalidSignature, path, logDir, dedupe, maxBodyBytes }) → an http.Server. See examples/basic-server.js for a full annotated example, including routing by topic.new IdempotencyStore({ ttlMs, maxEntries }) with .seen(id) → boolean, for dedupe logic you want to run yourself.replay({ file, url, secret, resign, webhookId }) — the function behind swt replay, if you want to drive it from a test script instead of the CLI.swt listen --secret $SHOPIFY_WEBHOOK_SECRET --log ./webhook-captures
swt replay ./webhook-captures/captures/orders_create__<id>.json --url http://localhost:3000/webhooks --secret $SHOPIFY_WEBHOOK_SECRET
swt sign examples/captured-webhooks/orders-create.sample.json --secret $SHOPIFY_WEBHOOK_SECRET
Run swt --help for the full flag list. There's also a ready-made sample payload at examples/captured-webhooks/orders-create.sample.json if you want to try listen + replay without a real store at all:
npx swt listen --secret devsecret &
npx swt replay examples/captured-webhooks/orders-create.sample.json --url http://localhost:3000/webhooks --secret devsecret
Verification must run against the exact bytes Shopify sent — not a JSON.parse'd-and-JSON.stringify'd copy. Re-serializing can reorder keys or reformat numbers, producing different bytes and a signature mismatch even for a genuinely valid webhook. If you're on Express, that means using express.raw({ type: 'application/json' }) on the webhook route specifically, not express.json(). examples/express-middleware.js shows the working pattern; test/verifyHmac.test.js has a test that reproduces the failure if you get this wrong.
createWebhookServer is a development/testing tool — verifyHmac and IdempotencyStore are exported separately so you can use the verification and dedupe logic inside your real framework/queue, but this package doesn't handle TLS, horizontal scaling, durable queues, or graceful shutdown for you.IdempotencyStore is in-memory and resets on restart. For production, back dedupe with something durable (Redis, a unique constraint in your database).shopify.app.toml) mutation, not something this package does.shopify app dev's built-in one, ngrok, etc) — swt replay exists specifically so you don't have to set one up just to iterate on a handler.Why not just use express.json() and compare the header myself?
You can — verifyHmac is exported standalone for exactly that. The gotcha above (raw body vs. re-serialized) is the part people get wrong, so it's worth using a tested function for it either way.
Does this handle Shopify's retry/backoff behavior?
It's aware of it: Shopify expects a response within a few seconds and retries on timeout, so createWebhookServer always answers before running your handler, and dedupes retries by X-Shopify-Webhook-Id so a retry doesn't run your handler twice. The exact timeout/retry-count numbers are Shopify's to change — check shopify.dev's webhook docs for the current values rather than hardcoding them into your app.
Can I use this with GraphQL Admin API webhook subscriptions, not just the legacy REST ones? Yes — the headers and HMAC scheme are the same regardless of how the subscription was created.
If webhooks are just one piece of a bigger app or store project, our free Shopify tools and store audit cover the rest of what we build day to day.
shopify-store-audit-toolkit — CLI that audits a live store's SEO, structured data, and performance signals.shopify-audit-mcp — connect Claude/Cursor/other MCP clients to the same audit as a callable tool.shopify-seo-checklist — interactive, open-source SEO checklist.shopify-cro-playbook — 75 conversion-rate checks across 14 categories.shopify-functions-examples — annotated Shopify Functions in Rust and JavaScript, the other half of "customize backend behavior" alongside webhooks.Issues and PRs welcome — especially real-world webhook payload shapes for the examples/captured-webhooks/ folder, or reports of anything in Shopify's webhook headers/behavior that's changed since this was written.
IdempotencyStore implementation.swt listen --forward <url> to fan a captured webhook out to multiple local handlers at once.MIT — see LICENSE.
This project is maintained by Ecom Swift LLC, a Shopify Partner.
Building or debugging a Shopify app and want a second pair of eyes on more than just webhooks? Get a free store audit (SEO, speed, CRO, AI visibility) or browse our other free Shopify tools. We're Ecom Swift LLC, a Shopify Partner Agency — www.ecomswiftllc.com.