Skip to content

About

A Shopify app (Remix) that forwards selected Shopify events to any HTTPS endpoint the merchant owns. Merchants pick their events with checkboxes, paste a destination URL, and every matching store event is relayed as a signed JSON payload — with a delivery log for every attempt.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Event Tracker — Webhook Triggers

A Shopify app (Remix) that forwards selected Shopify events to any HTTPS endpoint the merchant owns. Merchants pick their events with checkboxes, paste a destination URL, and every matching store event is relayed as a signed JSON payload — with a delivery log for every attempt.

Shopify store
    │  orders/create, products/update, carts/update, …
    ▼
/webhooks/events   (HMAC verified by @shopify/shopify-app-remix)
    ▼
Dispatcher         (signs, POSTs, retries, logs)
    ▼
Merchant endpoint

Stack

Piece Choice
Framework Remix 2 (Vite) — Shopify's official app stack
Shopify SDK @shopify/shopify-app-remix v5, Admin API 2026-07
Database Prisma + SQLite locally (swap for Postgres in prod)
Embedded UI App Bridge CDN + ui-nav-menu (no Polaris)
Auth Token exchange (unstable_newEmbeddedAuthStrategy)

Getting started

npm install
npm run setup      # prisma generate + migrate deploy
npm run dev        # shopify app dev (creates the tunnel, fills in .env)

npm run dev asks you to pick/create an app in your Partner account and writes the credentials for you. For a manual run, copy .env.example to .env and fill in SHOPIFY_API_KEY, SHOPIFY_API_SECRET, SHOPIFY_APP_URL.

Link this folder to an existing app instead:

npm run config:link    # shopify app config link
npm run deploy         # shopify app deploy

How it works

Pages (app/routes/)

Route Purpose
app._index Overview — stats, recent deliveries, setup checklist, test send
app.settings Destination URL + event checkboxes; saving syncs Shopify
app.logs Full delivery history with delivered/failed filters

Event subscriptions

Event webhooks are per shop, created from the settings page rather than declared in shopify.app.toml, because each merchant picks a different set. On save, syncSubscriptions() (app/lib/settings.server.js) reads the shop's current subscriptions and reconciles them:

  • ticked but missing → webhookSubscriptionCreate
  • ticked but pointing at a stale callback URL → webhookSubscriptionUpdate (this is what keeps a rotating shopify app dev tunnel working)
  • unticked → webhookSubscriptionDelete

Only lifecycle webhooks (app/uninstalled, app/scopes_update) live in shopify.app.toml.

Supported events

Group Events
Orders orders/create, orders/paid, orders/fulfilled, orders/cancelled
Products products/create, products/update, products/delete
Customers customers/create, customers/update
Carts carts/create, carts/update

Shopify has no universal add_to_cart webhook — carts/create and carts/update are the closest server-side signal. True add-to-cart tracking needs a Theme App Extension (see Roadmap).

Delivery

Every event lands on the single /webhooks/events endpoint, where the Shopify SDK verifies the HMAC. The route responds 200 immediately (Shopify times out at 5s) and hands off to the dispatcher, which:

  1. skips the event if the shop has no destination URL or the event is unticked
  2. POSTs a JSON envelope to the merchant's URL (10s timeout)
  3. retries up to 3 times (1s, then 5s) on network errors, 5xx and 429 — 4xx responses are treated as final and are not retried
  4. writes a DeliveryLog row with status, HTTP code, duration and attempt count

The newest 200 deliveries are kept per shop.

Payload

POST https://your-endpoint.example.com
Content-Type: application/json
X-Event-Tracker-Event: orders/create
X-Event-Tracker-Shop: my-store.myshopify.com
X-Event-Tracker-Signature: sha256=<hex hmac of the raw body>

{
  "id": "ca5afa62-…",              // unique delivery id
  "event": "orders/create",
  "shop": "my-store.myshopify.com",
  "sent_at": "2026-08-24T17:25:18.287Z",
  "data": { /* the raw Shopify webhook payload */ }
}

Verify the signature with the per-shop secret shown on the settings page:

const expected =
  'sha256=' + crypto.createHmac('sha256', SIGNING_SECRET).update(rawBody).digest('hex')
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(receivedHeader))

Data model (prisma/schema.prisma)

Model Holds
Session Shopify access tokens (managed by the SDK's session storage)
Shop One row per install
Setting Destination URL + per-shop signing secret
EventSetting Which events are on, plus the Shopify subscription id
DeliveryLog Status, HTTP code, response, duration and attempts per event

Before going live

  • Scopes: read_orders,read_products,read_customers. Order and customer data is protected customer data — request access in the Partner Dashboard.
  • Database: SQLite is single-file and local. Point the datasource at Postgres and re-run prisma migrate deploy for production.
  • Retries: they run in the app process. On a serverless host, move the dispatcher onto a real queue so retries survive a cold shutdown.

Roadmap

Shipped (MVP): admin settings, event checkboxes, per-shop subscriptions, signed delivery with retries, delivery logs, test event.

Next: Theme App Extension for true add-to-cart tracking, a durable retry queue, per-event filters, and billing.

prototype/

The original Vite + React mockup this app's design came from. It is standalone (its own package.json) and is not part of the build — kept as a visual reference. npm run dev inside that folder still runs it.

About

A Shopify app (Remix) that forwards selected Shopify events to any HTTPS endpoint the merchant owns. Merchants pick their events with checkboxes, paste a destination URL, and every matching store event is relayed as a signed JSON payload — with a delivery log for every attempt.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages