Most Shopify ↔ ERP integrations start working and then break in production. Not because the code is wrong — because the architecture doesn't account for the failure modes that only appear at real scale.
The fundamental problem: at-least-once delivery
Shopify delivers webhooks at least once — which means you can receive the same webhook multiple times. If your integration isn't idempotent, you'll create duplicate orders in your ERP, double-charge customers, and cause inventory discrepancies.
Idempotency: design it from day one
Every webhook handler must be idempotent. When the same event arrives twice, the second processing should produce the same result as the first — not a duplicate.
// Store processed webhook IDs to ensure idempotency
async function handleOrderCreated(webhookId: string, payload: OrderPayload) {
// Check if we've already processed this webhook
const already = await db.processedWebhooks.findUnique({
where: { webhookId }
});
if (already) return; // Already processed — skip
// Process in a transaction
await db.$transaction(async (tx) => {
await syncOrderToERP(payload);
await tx.processedWebhooks.create({ data: { webhookId, processedAt: new Date() } });
});
}Webhook vs polling: choose deliberately
- →Webhooks: real-time, efficient, but require reliable infrastructure and idempotency handling
- →Polling: simpler to implement, inherently idempotent if done correctly, but adds latency and wastes API quota
- →Best approach: webhooks as the primary mechanism, polling as a reconciliation job (runs every 15 minutes to catch missed webhooks)
Ordering guarantees (or lack thereof)
Shopify webhooks can arrive out of order. An order/updated webhook can arrive before the order/created webhook. Design your processing to handle this — check the updated_at timestamp and only process if the incoming data is newer than what you have.
The reconciliation job
No webhook-based integration is complete without a scheduled reconciliation job. Run a comparison between Shopify and your ERP every 15–60 minutes. Any discrepancies trigger a re-sync. This is your safety net for missed webhooks, failed processing, and ordering issues.
Building something like this on Shopify?
I build exactly what I write about. If you need help implementing this, get in touch — I respond within 24 hours.
Get new Shopify guides by email
Practical, technical articles on Shopify development — no fluff, no sales emails.