ERP Integration: Patterns That Work and Those That Don't
The ERP is the center, like it or not
The ERP is the gravitational system of any business. Everything orbits around it: sales feed invoicing, invoicing feeds accounting, accounting feeds reporting. When the ERP is isolated, the company operates with data silos and manual reconciliation processes. When it is well integrated, information flows automatically and human errors disappear.
The problem is that “well integrated” is rare. After implementing ERP integrations for clients using Holded, SAP Business One, Odoo, Xero, and Sage, we have seen the same success and failure patterns repeat. This executive brief documents both.
Patterns that work
API-first: the ERP as a service
The most solid pattern is treating the ERP as a service with an API (as we detail in our API-first design article), not as a database with an interface. All reads and writes go through the ERP’s API. No external system touches the database directly.
Why? Because the ERP’s API encapsulates business logic. When you create an invoice via API, the ERP applies numbering rules, calculates taxes, updates customer balances, and records accounting entries. When you insert a record directly into the database, none of that happens. And discovering a missing accounting entry three months later during an audit is not enjoyable.
The practical implementation:
- Integration middleware that centralizes communication. It can be as simple as a Python/Node service that orchestrates calls, or as comprehensive as an iPaaS (Workato, Make, n8n). What matters is that it is the single point of contact with the ERP API.
- Rate limiting and retry: cloud ERP APIs (Holded, Zoho, Xero) have call limits. 100 requests/minute is common. The middleware must implement queuing with exponential backoff. Without this, integrations fail at the highest-volume moments (month-end closings, campaigns).
- Idempotency: every write operation must be idempotent. If the integration retries an invoice creation because the timeout was ambiguous, it must not create a duplicate invoice. The pattern: check existence by external reference before creating.
Event-driven: react instead of poll
Instead of periodically querying the ERP (“are there new invoices?”), events allow the ERP to notify when something changes. It is a classic Enterprise Integration Pattern: the difference between polling and push. And the difference in latency and efficiency is dramatic.
Modern ERPs offer webhooks: Holded can send a POST when an invoice is created, Zoho when a contact is updated, Stripe when a payment is processed. The flow is:
- An event occurs in the ERP (new invoice)
- The ERP sends a webhook to the middleware
- The middleware processes the event (update CRM, send email, record payment)
- If processing fails, the event is queued for retry
Webhooks are not 100% reliable. They get lost, arrive duplicated, arrive out of order. The architecture must assume this:
- At-least-once delivery: design to receive the same event twice. Idempotency, again.
- Event store: save all received events, processed or not. Enables replay if something fails and is invaluable for debugging.
- Fallback to polling: even with webhooks, a reconciliation poll every hour catches lost events.
Bidirectional sync with source of truth
When two systems need to share data (the CRM has contacts, the ERP has customers), the temptation is bidirectional sync: changes on either side propagate to the other. Elegant on the diagram. Painful in production.
What happens when the same record is modified in both systems simultaneously? Who wins? The CRM updated the phone number and the ERP updated the email for the same contact at 14:03. Without a clear rule, synchronization becomes a conflict generator.
The pattern that works: define a source of truth per field, not per record. The CRM is the source of truth for commercial data (phone, email, owner). The ERP is the source of truth for financial data (balance, invoices, payment terms). Each field has an owner. Conflicts are impossible because no field is written from two places.
The implementation requires an explicitly documented field map:
| Field | Source of truth | Direction |
|---|---|---|
| Name | CRM | CRM -> ERP |
| CRM | CRM -> ERP | |
| Tax ID | ERP | ERP -> CRM |
| Payment terms | ERP | ERP -> CRM |
| Outstanding balance | ERP | ERP -> CRM (read-only) |
Patterns that do not work
Direct database integration
Connecting systems directly to the ERP’s database is the most common pattern and the most destructive. The reasons for doing it are understandable: the API is slow, the API does not expose all fields, the development team knows SQL better than REST.
The reasons not to:
- Bypassing business logic: as noted above, inserting data directly skips validations, calculations, and side effects that the application expects.
- Schema coupling: an ERP version upgrade modifies internal tables and breaks every integration. We have seen this happen with Sage upgrades that broke 4 integrations simultaneously.
- No audit trail: direct writes do not appear in the ERP’s activity log. For legal and compliance purposes (especially tax reporting obligations), this is a serious problem.
File-based integration (the CSV of doom)
Export data from the ERP to CSV, process it with a script, import the result back. A pattern that works exactly once: when you test it with 10 records. In production, with thousands of records, partial failures, and special characters in names, it is a constant source of incidents.
If CSV is the only option (some legacy ERPs offer nothing else), at minimum: validate the format before importing, log each processed line, and never, ever, partially process a file without a log of what was processed and what was not.
The “sync everything in real time” approach
The ambition to synchronize all data between all systems in real time seems logical. In practice, it generates exponential complexity, consumes ERP resources (which were not designed for 10,000 API calls per hour), and produces a cascade of events that can saturate the middleware.
The practical rule: synchronize in real time only what the business needs in real time. Orders, yes. The product catalog, probably not (a sync every hour is sufficient). The invoice history, definitely not.
The middleware as the critical piece
The integration middleware is the piece that determines whether the integration scales or collapses. The options:
Custom code (Python, Node): maximum control, minimal license cost, maximum maintenance cost. Appropriate when integration logic is complex and specific.
Low-code iPaaS (Make, n8n, Workato): fast to implement, visual, with predefined connectors for common ERPs. Appropriate for standard integrations. The risk: when logic gets complex, no-code becomes a node maze harder to maintain than the equivalent code.
ESB/integration platform (MuleSoft, WSO2): for organizations with 10+ integrations and governance requirements. Overkill for the typical SME.
Our recommendation for mid-sized European companies: start with custom code (a Python or Node service with a message queue) for the 2-3 critical integrations. If the number grows to 5+, evaluate an iPaaS. If it grows to 10+, consider an ESB.
The numbers that matter
Metrics that every ERP integration should monitor:
- Sync latency: time between the event at origin and the update at destination. For invoicing, it should be < 5 minutes. For catalog, < 1 hour is acceptable.
- Error rate: percentage of operations that fail. Target: < 0.1%. If it exceeds 1%, there is a design problem.
- Orphan records: data that exists in one system but not the other. A weekly reconciliation should detect them. Zero is the target; < 0.5% is acceptable.
- Retry queue: how many events are pending retry. If the queue grows consistently, the downstream cannot absorb the volume.
These metrics should be on a visible dashboard. Not in logs that nobody reads.
If your organization is planning an ERP integration or needs to improve existing ones, our custom development team designs and implements robust integrations. We also offer consulting to define the integration architecture before writing a line of code. For specific integrations with Holded and Zoho, we have direct experience documented in our logistics solutions.
About the author
abemon engineering
Engineering team
Multidisciplinary engineering, data and AI team headquartered in the Canary Islands. We build, deploy and operate custom software solutions for companies at any scale.
