The ERP Integration Patterns That Actually Hold Up at Scale
Odoo, Business Central, SAP S/4HANA, or a bespoke ERP nobody should still be running: most integration patterns are the same whatever the platform. Here is what holds up, and one pattern that ends in a production incident sooner or later wherever it is used.
How a bidirectional sync fails
A distributor runs a WMS (warehouse management system) alongside its ERP. Both systems need to know the current inventory count, so the integration team builds a bidirectional sync. When inventory changes in the ERP, the WMS is updated. When inventory changes in the WMS, the ERP is updated.
It works for months. Then one morning a WMS batch job and an ERP inventory adjustment run at the same moment on the same SKU. Both systems write their value. Both receive the other's update and write back. The loop runs hundreds of times before a circuit breaker trips and freezes both systems.
When the freeze clears, the inventory records for the affected SKUs are in an indeterminate state. Nobody knows which system holds the correct number. The only way out is a physical stock count and a full re-reconciliation of the affected records.
This is the pattern that kills systems: bidirectional sync without a canonical source of truth. The question "which system is correct when both have changed?" has no automated answer. Every integration that tries to answer it automatically will eventually produce a loop, a conflict, or corrupted data.
Pattern 1: event-driven over polling
Polling means asking the ERP "has anything changed since I last checked?" on a schedule. It is the default approach because it is easy to build. It is also consistently wrong in ways that compound over time.
Polling introduces latency proportional to your poll interval. If you poll every 5 minutes, every downstream system is 5 minutes behind. For a WMS that needs near-real-time inventory, a 5-minute lag is a restock decision made on 5-minute-old data. For a financial system that needs confirmed PO status, a 5-minute lag means approvals queue up and rush through together.
Polling also creates thundering herd problems at scale. Fifty concurrent poll jobs hitting the ERP API simultaneously every 5 minutes saturate the connection pool. The resulting ERP slowdowns get diagnosed as capacity problems when the real cause is the polling architecture.
The alternative: webhook-first design. When something changes in the ERP, the ERP pushes an event to your integration layer. Your integration layer processes it within milliseconds. No latency, no load spike, no thundering herd. Recent Odoo versions can send webhooks from automation rules. Business Central has Power Automate triggers. SAP S/4HANA has the Event Mesh. The polling fallback is for ERPs that do not support webhooks. Use it only when you must, and document it as technical debt with a plan to replace it.
Pattern 2: idempotent writes with correlation IDs
Every write to an ERP from an integration layer should be idempotent: sending the same write twice produces the same result as sending it once, with no duplicate records, no double-processing, no error.
This matters because network failures are real. A webhook fires, your integration layer processes it and sends a write to the ERP, the network drops before the ERP confirms receipt, your layer retries. Without idempotency, you now have a duplicate purchase order in the ERP. With idempotency, the retry is a no-op.
The mechanism: every write carries a correlation ID, a deterministic identifier derived from the source event (typically a hash of the source system ID and the event timestamp). Before writing, the ERP integration checks whether a write with this ID has already been processed. If yes, return the prior result. If no, process and record.
Our take
x-ms-deduplication-id header. Do not count on the ERP to deduplicate for you. Build it into your integration layer. The integration layer owns the correlation tracking; the ERP just receives writes.Pattern 3: retry queues separate from main processing
When an integration write fails because the ERP is unavailable, a rate limit was hit or validation failed, the natural instinct is to retry immediately in-line. Do not do this.
Inline retries block the main processing queue. If the ERP is down for 20 minutes, your main queue backs up with retrying items, new events cannot be processed, and when the ERP comes back you have a backlog that floods the system simultaneously. This is how a 20-minute ERP outage becomes a 4-hour operational disruption.
The pattern we use: failed writes go to a separate retry queue immediately, with an exponential backoff schedule (retry after 30 seconds, 2 minutes, 8 minutes, 30 minutes, 2 hours). The main queue continues processing new events without interruption. The retry queue runs independently and has its own circuit breaker. If the ERP is down, retries pause automatically instead of hammering an unavailable endpoint.
Items that exhaust the retry schedule, usually after 24 hours of attempts, move to a dead letter queue with a human review notification. The dead letter queue is visible to operations, has enough context to understand why the item failed, and has a one-click "retry now" when the underlying issue is resolved.
Solving the canonical source of truth problem
Back to the WMS/ERP inventory disaster. The fix is simple in principle and requires discipline in practice: define a canonical source of truth for every data entity, and make the integration flow one-directional from that source.
WMS owns inventory. ERP receives inventory updates from WMS on a defined schedule. ERP never writes back.
ERP owns POs. WMS receives PO data from ERP when confirmed. WMS never creates POs.
WMS records physical receipt. ERP receives goods receipt confirmation from WMS to match against PO.
There is no situation where both systems update the same entity. One system is always the authority. The integration is always a read-from-source, write-to-receiver flow. When the business says "both systems need to be able to update this," the answer is to choose which system is the authority and redesign the workflow in the other system to submit requests to the authoritative one.
Every time someone says "both systems need to be able to update this," ask what happens when both update it at the same time. If the answer is "we will handle conflicts," you are building the system that looped itself into a stock count.
What changes by platform
The patterns above are platform-agnostic. The implementation varies:
Odoo: The XML-RPC API is stable but old. The JSON-RPC API is faster and preferable for new integrations. Recent versions can send webhooks for outbound events. Bulk operations via the API are limited by the server configuration, so know your limits before building high-volume sync.
Business Central: The OData API is well-documented and reliable. The AL extension model means you can build custom API endpoints if the standard ones do not expose what you need, which is the right approach rather than trying to query tables directly. The sandbox environment is useful for integration testing.
SAP S/4HANA: The API surface is complex: SOAP BAPIs, REST OData, IDocs. The Event Mesh is the right architecture for real-time integration. Expect to spend real time working out which API endpoint maps to which business object before you write a line of integration code. Do not skip that step.
Related reading