Shopify GDPR Webhooks - How ERPClaw Handles Data Requests + Redaction
How the ERPClaw Shopify integration handles the four mandatory GDPR webhooks: customers/data_request, customers/redact, shop/redact, app/uninstalled. Source-verifiable + audit-ready.
Shopify mandates four compliance webhooks on every published app. This page documents how ERPClaw handles each one, what data we hold where, and what your obligations are as the data controller.
The four webhooks
| Topic | Fired when | Worker behavior | ERPClaw behavior |
|---|---|---|---|
customers/data_request | Merchant requests data export for a customer | Verifies HMAC, queues gdpr-dispatch for your ERPClaw, returns 200 | Generates JSON export of all customer-related rows, mails to merchant |
customers/redact | 10 days after merchant-requested customer deletion | Verifies HMAC, queues gdpr-dispatch, returns 200 | Redacts customer PII from local DB; preserves anonymized order/financial records |
shop/redact | 48 hours after shop uninstall | Verifies HMAC, deletes all *:{shop} KV keys, queues final gdpr-dispatch for your ERPClaw, returns 200 | Receives final cleanup signal; merchant decides what to redact locally |
app/uninstalled | Immediately on app uninstall | Verifies HMAC, drops KV state for the shop, returns 200 | Local ERPClaw data preserved; merchant retains control |
Verification is constant-time HMAC-SHA256 against the
SHOPIFY_CLIENT_SECRET environment variable, per Shopify’s
webhook signature spec.
Why the Worker forwards instead of acting
The Worker holds no merchant business data: no customer names, no order amounts, no financial totals. So when Shopify asks “give me everything you have about customer X”, the Worker has nothing to give.
But your ERPClaw does. So the Worker queues a gdpr-dispatch command
in Cloudflare KV with the shop, the topic, and the inbound payload.
Your ERPClaw picks it up on the next status check-in (or via SSE if
active), runs the actual data request or redaction against its local
SQLite database, and emails the result to the merchant.
This means:
- No customer data lives on our infrastructure, so we have no GDPR Article 30 record-of-processing obligation for the bulk of the data.
- Your ERPClaw is the data controller, and you handle the GDPR obligations for the data you hold.
- The Worker is only a relay; we hold data minimization to the furthest extent possible.
What ERPClaw redacts on customers/redact
When the customers/redact command reaches your ERPClaw, the
following happens locally:
- The customer’s
customer.name,customer.email,customer.phone,customer.billing_address,customer.shipping_addresscolumns are set to anonymized placeholders (e.g.,name = 'REDACTED-{customer_id}'). - Linked rows that contain customer PII (sales invoices, sales orders,
delivery notes) have their
customer_namefield anonymized too. - GL entries are preserved. ERPClaw’s general ledger is immutable by design; redacting GL entries would break the chain of custody and violate accounting record-keeping requirements (US GAAP, IRS Pub 583, GDPR Article 17(3)(b) exception for legal obligations).
- Stock ledger entries (SLE) are preserved for the same reason.
- The redaction event is logged in
data_audit_logso you can prove the request was honored.
What shop/redact actually deletes
When shop/redact fires (48 hours after uninstall), the Worker
deletes:
meta:{shop}(shop domain, owner email)status:{shop}(status blob with sync counters)pair:*keys still associated with the shop (should be empty by then)- Any pending
cmd:*:{shop}command queues - Any pending
dedup:*:{shop}deduplication state
ERPClaw receives a gdpr-dispatch command with topic shop/redact.
By design, ERPClaw does not delete your local SQLite data on this
signal. The merchant’s local install is theirs to control. You can
delete the local DB manually if you want a clean wipe.
Data we never hold (GDPR negative-confirmation)
For audit purposes, here is what does not exist anywhere on our Worker, never has, and never will:
- Customer names, emails, phone numbers, addresses
- Order line items, amounts, products
- Product details, SKUs, prices
- Inventory levels, warehouses
- Financial entries (invoices, payments, GL entries)
- Payout amounts (we hold counters, not amounts)
- Tax IDs, tax registration numbers
You can verify this by reading the Worker source at github.com/avansaber/erpclaw-addons and grepping for any of the above field names. They do not appear.
Audit trail
If you need a GDPR Article 30 records-of-processing trail for an audit, the following are available:
- Worker access logs: Cloudflare Logpush to R2, 90-day retention.
- GDPR webhook receipt log: every received webhook is logged with topic, shop, timestamp, and HMAC verification result. Available in
gdpr_webhook_logCloudflare KV index. - ERPClaw
data_audit_log: every redaction or data request your ERPClaw processes is logged locally with timestamp, customer_id, requestor email, and outcome.
Where to test the flow
The shopify shopify-handle-gdpr action accepts a --topic and
--payload for offline testing. See the action documentation in the
SKILL.md source.
Privacy policy
Our public privacy policy for the Shopify integration is at avansaber.com/privacy-shopify.
For questions about the GDPR webhook flow, email
[email protected].