Skip to main content
More Shopify documentation

Shopify Integration Architecture in ERPClaw

Technical architecture of the ERPClaw Shopify connector: Cloudflare Pages plus Worker plus your ERPClaw. Orders and customer records sync directly, not via our servers.

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 or PostgreSQL on your machine)     direct connection

The critical invariant: order, customer, and product sync never transits the Worker. It flows on the bottom arrow, directly between your ERPClaw and Shopify. What the Worker does handle is the one-time token handoff, the status counts, and Shopify’s compliance requests (covered below).

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, issues a signed, timestamped state value (its nonce kept in KV for five minutes, single use), 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 Shopify’s HMAC and the state, 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, info="status-push:<shop>", L=32). (When the app loads embedded in Shopify Admin without this redirect, the admin UI gets the same kind of pairing code from POST /pair/init, which uses Shopify’s token exchange instead of the code grant.)
  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 overwrites the KV entry with a short-lived “consumed” marker. The code is single-use: a second read gets an error, not the token.
  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: the Worker keeps no customer records of its own. It queues the request, with Shopify’s payload (the customer’s identifiers and any affected order IDs), as a gdpr-dispatch command, where it waits until your ERPClaw collects and acknowledges it.
  • customers/redact: same path; your ERPClaw records the request in its audit log, and the redaction itself is a step you run.
  • shop/redact: fired 48 hours after uninstall. The Worker queues it for your ERPClaw and deletes the shop’s status blob, HMAC secret, and install state from KV. ERPClaw preserves GL entries (immutable; covered by GDPR Article 17(3)(b) exception for tax and accounting).
  • app/uninstalled: the Worker queues it for your ERPClaw; the shop’s KV state is cleared when shop/redact arrives.

Data location summary

Data Where it lives Retention
Shop domain Cloudflare KV key names (status:, shop:, commands:) Until shop/redact
OAuth access token Cloudflare KV (pair:{code}) during handoff only 10 min max, removed on redemption
Per-shop HMAC secret Cloudflare KV (shop:{shop}:hmac), plus a copy held by your ERPClaw On the Worker until shop/redact (it verifies your status pushes); indefinite on your ERPClaw
Status blob Cloudflare KV (status:{shop}) One hour, refreshed on each push
Queued commands, including compliance request payloads Cloudflare KV (commands:{shop}) Until your ERPClaw acknowledges them, 28 days at most
Orders, customers, products Your ERPClaw database (SQLite default or PostgreSQL) Indefinite; you control it
GL entries Your ERPClaw database (SQLite default or PostgreSQL) Indefinite; immutable
Worker access logs Cloudflare Logpush to R2 90 days

Source of truth