Integrating with an ERP: what the other system decides for you
Pedro CunhaPublished on updated on 13 min read
In short
The architecture of an integration is not your choice: it is imposed by what the other system offers. Across three integrations we built — NextFit, Altimus and Controlle — the same question (what changed over there?) had three different answers, because the sources were different. And whatever the source does not provide becomes a limit on your product, not just a line of code.
"Does it have an API?" is the wrong question#
When a company buys a new system that has to live alongside the ERP it already runs, the first technical question is almost always the same: "does it have an API?". The answer is yes or no, and neither one tells you what actually decides the project.
We have built three integrations with third-party systems, all of them in production:
- NextFit, the ERP of a gym chain, where we read the sales that feed the chain's commercial targets.
- Altimus, the vehicle inventory system of a dealership, where we read the vehicles that go into a financing application.
- Controlle, the financial ERP our own company runs, where we read entries and balances for the partners' dashboard.
The three gave different answers to the questions below — and every "no" turned into code on our side.
| What you ask the source | If the answer is "no", you build | Where we hit it |
|---|---|---|
| Can it tell you only what changed? | the change detection itself, on your side | Altimus |
| Does the query window have a time, or only a date? | a local cut with a safety margin | NextFit |
| Does it tell you when a record is deleted? | reconciliation by absence | Altimus · Controlle |
| Does it know your operation's structure (branch, unit)? | origin stops coming from the payload and starts coming from the credential | NextFit |
| Does its record count cover every type of record? | pagination until the empty page, ignoring the total | Controlle |
| Is the rate limit documented? | your own measurement and automatic adaptation | NextFit · Controlle |
None of these fit inside "does it have an API?". All of them change the timeline, and some change the scope.
How to know something changed on the other side#
None of the three sources notifies you when something happens. There is no webhook in any of them, so all three integrations are what Enterprise Integration Patterns, by Gregor Hohpe and Bobby Woolf, calls a Polling Consumer: your system is the one that asks, at a pace it chooses.
From there, each source allows a different level of precision:
- When the source accepts a filter by last modified date, you ask only for what changed since the previous read. That is the NextFit case.
- When it accepts a date range, you work with a moving window and reprocess the whole interval. That is the Controlle case.
- When it filters nothing, you read the entire database and compare record by record against what you already stored. That is the Altimus case.
The detail that usually escapes notice shows up in the first case. The NextFit window is by date, without time — so every read returns the whole day again. Writing it all back would be wasted work, so we cut locally what changed since the last sync, with a margin of a few minutes backwards. The margin covers clock drift and the record modified at the exact instant the previous read was finishing. Re-reading is harmless because the write is idempotent — the same record read twice produces the same result — which is the Idempotent Receiver pattern from the same catalogue.
And there is a hole that only surfaces months later, when nobody is watching: incremental synchronisation never recovers what you lost. If an older record failed to be stored for any reason, its modified date is already in the past, and no future incremental read will bring it back. Both projects that use incremental sync had to gain a sibling: in the gym chain, a full-load mode for the period; in the financial dashboard, a weekly full reload that ignores the incremental logic and sweeps months backwards.
The rule we took from it: every incremental sync needs a sibling that ignores the incremental. Without it, the silent error is permanent.
How to know something was deleted on the other side#
This is the question that almost never makes it into the scope, and the one that causes the most trouble. A data source is good at telling you what exists; almost none of them tell you what stopped existing.
The way out is reconciliation by absence: whatever did not appear in the complete read is marked inactive on your side. That is how we know a vehicle left the dealership's inventory — the source system never sends "removed", it simply stops sending that record.
The same technique, applied carelessly, deletes what it should not:
- If the read covers a window, the deactivation has to be scoped to that same window. In the financial dashboard, an entry disappearing from the last few months means it was removed; an entry from three years ago not being there simply means it falls outside the range. Deactivating by absence without a scope would wipe out the entire history.
- Deactivation can only happen after the read finishes completely. A load interrupted halfway must never be read as "everything missing is gone".
When the ERP has no API#
The car dealership runs Altimus to manage its inventory. We asked for API access. Altimus does not have one, and does not open it — that is the vendor's decision, and they are under no obligation to change it because a customer asked.
What did exist was an address that returns the entire inventory as JSON. That is not an API: it does not filter by date, does not announce removals, has no published contract. The decision to build on top of it was made jointly with the client — model something sensible around what exists and sync periodically, rather than wait for an opening that was not coming.
What that integration bought was specific and valuable: re-entering vehicle data was over. Whoever assembles a financing application types the plate or the chassis number, and the rest of the vehicle data arrives already filled in. The step of copying by hand, from one system to the other, data that was already typed simply ceased to exist.
What it charged, in code that would not exist if there were an API:
- change detection, comparing each item's update date against the stored one;
- reconciliation by absence, to find out what left the inventory;
- photos downloaded and re-hosted in the client's own cloud, because a third party's image address is outside your control;
- fault tolerance per item, so one odd record does not bring down the whole load;
- visible progress and a stop button, because the load is long enough that someone will need to interrupt it.
The "no" worth recording: an export is not an integration — but it works. The mistake is not accepting the export; it is treating it as equivalent to an API when the scope is being signed.
How much of your product the ERP decides#
This is the expensive part, and the one almost no material about integration mentions.
At the dealership, the plan was not to stop at inventory. The integration was also going to reach the customer base, and from there other flows between the two systems. It stopped at inventory, because inventory was the only data the source delivered. The vendor's limit became the product's limit.
It still shows on screen today: the inventory module is fed entirely by Altimus — there is no manual vehicle registration inside it. But not every financed vehicle exists in Altimus. So a financing application accepts a vehicle that lives only inside it, and the shortcut to the full inventory record simply does not appear in those cases. That is what a source that does not cover everything looks like, in the interface.
At the other extreme, the gym chain shows what becomes possible when the source delivers: because NextFit has a real API, we could establish that no sale enters the new system except through NextFit. The ERP remains the owner of the data, and the new platform never accepts a manual entry for it. That is what stops the number from depending on who typed it.
Even with an API, the source keeps traps. In that integration, a gym-partnership sale arrives carrying the same marker that identifies a membership sale — and the order in which you test the conditions decides whether the partnership counts towards the target. A classification written in the intuitive order would have inflated every unit's target, silently and permanently. Finding that out is not reading documentation: it is opening the real data before writing the rule.
How the integration fails without taking your system down#
The system on the other side will go down, change a field without warning, or return errors for an hour. That is not a hypothesis, it is routine. What you design is the behaviour of your system when it happens:
- The sync never takes the process down. It records the failure, stores the message and returns a result that says "this did not work" — while the screens keep serving the last synchronised data.
- Errors are handled per item, not per load. A record with an unexpected shape is logged and skipped; the other thousands carry on.
- The alert fires on consecutive failures, and only once. In the financial dashboard the warning goes out when the consecutive-failure counter hits the threshold — and does not repeat on every subsequent failure. An alert that repeats becomes an alert that is ignored.
- Orphan runs have to be cleared. A process that dies mid-run leaves the sync marked as "running" forever, and the lock that prevents concurrent executions starts blocking every future one. The financial dashboard clears stuck runs on start-up.
- No response bodies in the log. When the data is financial, the log keeps route, status and duration — never the content, never the credential.
How often to synchronise#
There is no such thing as "the integration's frequency". There is one frequency per type of data, and the financial dashboard makes it obvious by running three at once:
| Type of data | Pace | Why |
|---|---|---|
| Catalogues (accounts, categories, cost centres) | once a day | they rarely change, and an off-hours change does not change a decision |
| Transactions | every hour | this is what the partner looks at to decide |
| Full reload | once a week | it is the sibling that fixes what the incremental let through |
Two criteria close the choice, and neither of them is technical.
The first is the operation's schedule. At the gym chain the read runs every few minutes, but only during business hours: a gym does not sell memberships at 3 a.m., and sweeping all night produces empty reads — work and cost with no new information.
The second is the nature of the data. Financial data has a future: an instalment due next month and a scheduled entry exist in the system before they happen. That is why the financial dashboard's window looks backwards and forwards, while the sales window only looks backwards — a future sale does not exist. It is not the API that defines your sync window; it is the nature of what you are reading.
One note on cost: when access to the source is paid, automatic synchronisation in both integrations ships switched off by default and is turned on deliberately, rather than starting to consume on the first deploy.
Frequently asked questions#
Can the new system replace the ERP I already use?#
It can, but it rarely pays off. An established ERP carries years of tax, accounting and operational rules nobody wants to rewrite. The pattern that works is the opposite: the ERP stays the owner of the data that is already its own, and the new system solves what the ERP does not — the reporting, the target, the dashboard, the workflow the team runs on the side. When each piece of data has one clear owner, the two coexist without drifting apart.
What if my ERP has no API?#
You can still integrate, with less reach. Many systems offer some form of export — a file, an address that returns the whole database, a scheduled report. That is not an API: it does not tell you what changed or what was deleted, so that work becomes yours. It works well for data that changes slowly and is read far more often than written, like a catalogue or an inventory. It does not work for a flow that needs an immediate answer.
How often will the data be up to date?#
It depends on the type of data, not on the integration. Within one application it makes sense to refresh catalogues once a day, transactions every hour, and reload everything once a week. Data the team looks at to decide something within the same shift calls for minutes; reference data calls for a daily cycle. Picking one frequency for everything wastes reads on one side and lags on the other.
Can the integration overload my ERP?#
It can, and it is the most underestimated risk. If every screen in the new system queries the ERP live, a usage spike in the new system becomes a request spike in the system the whole company depends on. The safe approach is to mirror: one process reads the ERP at a controlled pace, writes a local copy, and every screen reads the copy. The ERP then receives a predictable volume, no matter how many people opened the dashboard.
What happens to my system if the ERP goes down?#
With a mirrored architecture, almost nothing: screens keep serving the last synchronised data, and the system records that the sync failed instead of breaking. What must never happen is a third party's failure taking your process down — or turning into silence. Our rule is to alert after a few consecutive failures, once, so the warning keeps meaning something.
Sources#
- Enterprise Integration Patterns — "Idempotent Receiver", by Gregor Hohpe and Bobby Woolf, on receiving the same message more than once without side effects — https://www.enterpriseintegrationpatterns.com/patterns/messaging/IdempotentReceiver.html
- Enterprise Integration Patterns — "Polling Consumer", by the same authors, on the consumer that decides when to fetch — https://www.enterpriseintegrationpatterns.com/patterns/messaging/PollingConsumer.html
Next step#
Before signing the scope of any system that has to live alongside an ERP, put the six questions at the top of this article to the source's vendor, in writing. They cost one email and they decide weeks of work — including the chance to find out, as we did, that the answer is "we don't have one and we are not opening it".
Both cases mentioned here are published: the gym chain, whose commercial targets now come straight from the ERP, and the dealership, whose financing platform reads inventory from the management system. This is the kind of work we cover under integrations and APIs — and if your case is less about coexisting and more about replacing, it is worth reading when a legacy system needs replacing.