Skip to main content

Shopify Integration Architecture - How ERPClaw Connects to Shopify

Technical architecture of the ERPClaw Shopify connector. Cloudflare Pages + Worker + your ERPClaw. Merchant data never transits our servers. Three latency tiers for command delivery.

This page explains what runs where, which boundary each piece of data crosses, and why the design puts merchant business data on the merchant’s own machine instead of on our infrastructure.

Three components

The connector has three moving parts and nothing else.

  1. Cloudflare Pages at admin.shopify.erpclaw.ai. Serves the embedded admin UI (Polaris plus App Bridge v4). Loaded inside an iframe by Shopify Admin.
  2. Cloudflare Worker at shopify.erpclaw.ai. A small Hono app that handles the OAuth handshake, hands out pairing codes, receives status pushes, verifies GDPR webhooks, and delivers queued commands.
  3. Your ERPClaw. Runs on a machine you control. Owns the merchant business data: orders, customers, products, GL entries, inventory. Talks to the Shopify Admin API directly using an OAuth token stored locally.
  Shopify Admin (iframe)
        |
        v
  Cloudflare Pages  <----UI assets----
  admin.shopify.erpclaw.ai
        |
        | App Bridge JWT
        v
  Cloudflare Worker  <---OAuth, webhooks, status, commands---
  shopify.erpclaw.ai
        |
        | pairing code redemption (one time)
        | status push (every 15 min)
        v
  Your ERPClaw  <===Shopify Admin API 2026-04===>  Shopify
  (SQLite on your machine)     direct connection

The critical invariant: merchant business data (orders, customers, products) never transits the Worker. It flows on the bottom arrow, directly between your ERPClaw and Shopify.

Data flow during install (OAuth)

  1. Merchant clicks Add app in the Shopify App Store.
  2. Shopify redirects to shopify.erpclaw.ai/oauth/install?shop=<shop>.myshopify.com. The Worker validates the shop domain against a strict regex, sets a __Host- prefixed state cookie, and redirects to Shopify’s OAuth consent screen.
  3. Merchant approves. Shopify posts back to /oauth/callback with an authorization code and the state.
  4. The Worker verifies the state HMAC, exchanges the code for an offline access token, and stores it in Cloudflare KV under pair:{code} with a ten-minute TTL. Alongside the token it stores a per-shop HMAC secret derived via HKDF-SHA256(master, salt=shop, info="erpclaw-status-v1", L=32).
  5. The admin UI loads and shows the six-character pairing code.
  6. Merchant runs erpclaw shopify-connect --pairing-code <code> locally. ERPClaw calls GET /pair/<code>. The Worker returns the OAuth token and the per-shop HMAC secret, then synchronously deletes the KV entry. The code is single-use.
  7. From this point ERPClaw holds the token. It calls api.shopify.com/admin/api/2026-04/graphql.json directly.

If the ten-minute window lapses, the code is gone and the merchant generates a fresh one.

Data flow during steady state

Your ERPClaw pulls Shopify data on its own schedule using its local OAuth token. The Worker is out of the data path.

Every 15 minutes your ERPClaw POSTs a small status blob to shopify.erpclaw.ai/status/<shop>. The blob contains:

  • ERPClaw version string
  • Timestamp of last successful sync
  • Count of orders synced in the last 24 hours
  • Count of GL entries posted in the last 24 hours
  • Integer error count
  • Local URL for the Open ERPClaw button

The push is signed with the per-shop HMAC and a timestamp (±300 second tolerance). The Worker verifies it, writes it to KV under status:{shop}, and uses it to render the admin UI status card. No customer content, no order content, no financial amounts.

Three latency tiers for command delivery

The admin UI can queue commands (Sync now, Disconnect). The Worker delivers them to your ERPClaw in one of three ways:

Active tier (SSE): if your ERPClaw is online and holds an open EventSource to /events/<shop>, the Worker pushes commands immediately. Latency: a few hundred milliseconds.

Scheduled tier (15-minute push): when ERPClaw posts its next status blob, the Worker piggybacks any queued commands in the response body. Latency: up to 15 minutes. This is the default.

On-demand tier: the CLI command erpclaw shopify-flush-pending-events forces an immediate round trip. Useful from scripts or cron jobs.

All three tiers use the same command schema and the same acknowledgement mechanism. Delivery is at-least-once; commands carry an ack_id for deduplication.

GDPR webhooks

Shopify requires four compliance webhooks. All four hit the Worker. The Worker verifies the X-Shopify-Hmac-Sha256 signature (constant-time compare against SHOPIFY_CLIENT_SECRET) before any processing.

  • customers/data_request: Worker holds no customer data; queues a gdpr-dispatch command for your ERPClaw.
  • customers/redact: same path; your ERPClaw handles the actual redaction.
  • shop/redact: fired 48 hours after uninstall. Worker deletes all *:{shop} KV keys. ERPClaw preserves GL entries (immutable; covered by GDPR Article 17(3)(b) exception for tax and accounting).
  • app/uninstalled: Worker drops KV state immediately.

Data location summary

DataWhere it livesRetention
Shop domain, shop owner emailCloudflare KV (meta:{shop})Until uninstall + 30 days
OAuth access tokenCloudflare KV (pair:{code}) during handoff only10 min max, deleted on redemption
Per-shop HMAC secretKV during handoff, then held by your ERPClaw10 min on Worker, indefinite on your ERPClaw
Status blobCloudflare KV (status:{shop})30-day rolling
Orders, customers, productsYour ERPClaw SQLite databaseIndefinite; you control it
GL entriesYour ERPClaw SQLite databaseIndefinite; immutable
Worker access logsCloudflare Logpush to R290 days

Source of truth