
With a bespoke API layer acting as the meeting point between systems: one source of truth per entity, e-commerce → middleware → ERP → warehouse → courier, real time where it matters and batch where it is enough.
You integrate them by building a layer of custom APIs — a middleware — that acts as the meeting point between the systems instead of wiring them together in pairs. For each entity (customer, item, price list, stock, order) you establish which system holds the truth, you define the data contract, you develop the middleware that exposes REST or GraphQL APIs and propagates events via webhooks, you choose per flow between real time and batch, and you add queues, retries, idempotency and logs. The typical result is one single chain: e-commerce → API middleware → management system/ERP → warehouse → courier, with the same data travelling through every system without anyone retyping it.
The difference between an integration that holds and one that breaks every week is not the protocol you pick, but two decisions taken before any code is written: who holds the truth for each piece of data and what happens when something goes wrong. Anyone can write a flow that works on a good day; a production integration is one that notices by itself when it stops working. Codebaker, a software house based in Bologna and founded in 2019, designs integrations on exactly this basis — including towards management systems that expose no modern APIs.


The most natural way to start — wire the e-commerce straight to the management system, then the management system to the warehouse, then the e-commerce to the courier — is also the one that ages worst. With four systems connected in pairs there are six possible links, with five there are ten: every new sales channel multiplies the work, and every upgrade of one system risks breaking several flows at once. A central API layer flips the geometry: each system talks only to the middleware, which knows everyone's formats and translates. Adding a marketplace, a second courier or a CRM then means writing a single adapter, not rewriting the existing integrations. It is also where logs, alerts and reconciliation concentrate: one single place to look when an order has not arrived.


«Our management system is old, it cannot be connected to anything» is the sentence we hear most often, and it is nearly always false. A system that exposes no REST APIs still exposes something: its data, callable native procedures, structured files, services that can be surfaced. The work consists in building an integration layer that talks to the system in the ways it does support and translates everything outward into clean, documented APIs. The legacy system is neither modified nor replaced: it keeps doing exactly what it has always done, while the rest of the company stops depending on its limits. At Conor this meant connecting a B2B e-commerce with native iOS and Android apps to an AS400 system, in real time, for more than 5,000 customers a day and over 300,000 orders a year.
This is the journey an order takes in a well-designed integration, from the customer's click to the delivery notification.

The customer orders while seeing their own price list and real availability, because prices and stock already arrive from the management system through the middleware. The order is created with a unique identifier.

The API layer receives the order, checks the data against the business rules (credit limit, minimum order, blocked items), converts formats and queues it towards the management system.

The order enters the system that holds the truth on customers and documents, exactly as if the back office had typed it in. No double data entry, no copy-pasting from an email.

The picking list reaches the handhelds or the shop-floor terminals; the quantities actually picked travel back, update stock and generate a partial order if something is missing.

The middleware calls the carrier's APIs, generates the labels and receives the tracking code, which is written both onto the document in the ERP and into the customer's private area.

Delivery status, delivery notes and invoices climb back up the chain and become visible to the customer without anyone sending an email. Every step is traced, with logs and alerts on interrupted flows.
Every step rests on skills we cover in-house: from custom web APIs to custom e-commerce development, from payment gateway integration to legacy software modernisation.
Before any technology talk, this has to be settled: for each entity, which system is in charge, in which direction the data travels and whether real time is genuinely needed.
| Entity | Source of truth | Direction | Recommended frequency |
|---|---|---|---|
| Customer master data | Management system / ERP | ERP → e-commerce | Frequent batch, real time on credit limits |
| Item catalogue | ERP or PIM | ERP → e-commerce | Batch (several times a day) |
| Price lists, including customer-specific ones | Management system / ERP | ERP → e-commerce | Real time |
| Warehouse stock | ERP / WMS | ERP → e-commerce | Real time |
| Orders | E-commerce (then ERP) | E-commerce → ERP | Real time |
| Delivery notes and invoices | Management system / ERP | ERP → customer area | Daily batch |
| Delivery status | Courier | Courier → middleware → ERP and customer | Webhook, as soon as available |
| Returns and credit notes | Management system / ERP | Two-way | Batch, with reconciliation |
The rule of thumb: real time on whatever blocks a sale (price, availability, credit limit, order), batch on everything else. Demanding real time everywhere raises cost and fragility without adding value.
All four routes are legitimate, but they hold under different conditions. Here is how they behave on the criteria that matter once the integration is in production.
| Criterion | CSV/EDI files over FTP | Standard connectors / iPaaS | Point-to-point APIs | Bespoke middleware (Codebaker) |
|---|---|---|---|---|
| Initial cost | Low | Low-medium | Medium | Medium-high, but reusable |
| Recurring cost | Almost none | Fee by volume or connector | Maintenance only | Maintenance only, no subscription |
| Real time | No | Partial | Yes | Yes, where it genuinely matters |
| Error handling | Manual, after the fact | Whatever the product provides | To implement for every pair | Centralised: queues, retries, idempotency, alerts |
| Unusual business rules | Hard | Only if the connector foresaw them | Yes, but duplicated on every flow | Yes, in a single place |
| Legacy systems with no APIs | Often the only quick route | Rarely supported | Complex | Yes: a dedicated layer, as on AS400 |
| Scaling to new systems | Low | Good if the connector exists | Low: links grow exponentially | High: one adapter per new system |
| Ownership and lock-in | No lock-in | Dependency on the platform | Your code | Your code, standard and open technologies |
If the customer record can be edited both in the e-commerce and in the management system, sooner or later the two versions drift apart and nobody knows which one is right. For every entity, exactly one source system must be designated; the others read. It is the cheapest decision to take at the start and the most expensive to repair afterwards.
When integration is postponed to the end of the project, acceptance testing reveals that an indispensable field does not exist in the source system, or that a business rule makes the planned flow impossible. It is the single most frequent cause of overrun in the projects we inherit. The data contract must be written before either end is developed.
The complete order that leaves, arrives and gets delivered is ten per cent of the work. The rest is the edge cases: partial order, item out of stock after confirmation, order amendment, return, customer blocked for exceeding their credit limit, price changed between basket and confirmation. If they are not defined during analysis, the code will define them at random.
Systems are not always reachable and messages sometimes arrive twice. Without queues and retries, two minutes of unavailability become a lost order; without idempotency, a repeated webhook becomes a duplicated order. Both are cheap to design in and very expensive to add after the first incident.
An integration that stops silently is worse than one that does not exist, because the company keeps trusting the data. You need searchable logs, an error dashboard and an alert that reaches somebody: an interrupted flow must be discovered by the system, not by the customer phoning in.
Native iOS and Android apps plus responsive web, synchronised in real time with an AS400 system: catalogue, price lists, orders and delivery status. 5,000+ customers a day, 300,000+ orders a year, live in 6 months.
Custom IAM integrated with SAP, the HR system, Active Directory and Office 365: automatic provisioning of accounts, permissions, badges and mailboxes. 2,000+ users, -95% IT onboarding effort.
Movement data captured in the field and reconciled into a single system: 25 million crates, -99.9% losses, 3 years in production.
A Flutter app that talks to the dispensers over Bluetooth Low Energy and syncs with cloud APIs, working offline too: 10,000 users a day.
The integration layer, the code and the documentation remain the client's property. Further reading in the article one single specification for backend, React, Flutter and AI and on the page about custom management software and ERP.
The point where most projects get lost is that «we integrate the e-commerce with the ERP» is not a requirement: it is an intention. The requirement is the data contract — the exact list of fields that travel, their format, which ones are mandatory and what happens when one of them is missing or inconsistent. It is written before either end is developed and it is versioned the way code is versioned. Here is what an order entering the middleware looks like, with the fields that in practice cannot be missing.
POST /v1/orders
Idempotency-Key: ORD-2026-000184 // same value = same order
{
"externalReference": "ORD-2026-000184", // id in the source system
"channel": "b2b-ecommerce",
"customer": { "erpCode": "C00412", "vatNumber": "IT01234567890" },
"orderDate": "2026-09-10T09:41:00+02:00",
"currency": "EUR",
"lines": [
{
"itemCode": "ART-77120",
"quantity": 24,
"unitOfMeasure": "PC",
"unitPrice": 12.40, // price shown to the customer
"priceListApplied": "PL-CUS-C00412",
"discountPercent": 5
}
],
"netTotal": 282.72, // recomputed and verified by the middleware
"delivery": { "type": "carrier", "addressId": "SITE-2" },
"notes": "Deliver by Friday"
}Three details separate this payload from one that will generate phone calls. The Idempotency-Key: if the network drops after the request is sent but before the response arrives, the client retries with the same key and the ERP recognises the order already exists instead of creating a second one. The external reference: every document stays linked to the originating order, and that is what makes reconciliation possible. The transmitted price: the middleware does not trust the total coming from the front end, it recomputes it against the customer's price list and, if the two do not match, blocks the order instead of writing it wrong — the edge case of the price changing between cart and confirmation.
The error policy has to be decided deliberately, not left to the default behaviour of the HTTP library. This is the matrix we apply as a starting point and then adapt to the receiving system.
| Outcome | What it means | Middleware behaviour |
|---|---|---|
| 2xx | Accepted by the receiving system | Store the returned id and close the queued message |
| 4xx validation | The data is wrong: unknown customer, blocked item, credit limit exceeded | No retry: retrying will not change the outcome. The message goes to the error queue and notifies someone who can fix it |
| 409 / duplicate | The idempotency key already exists | Success, not failure: the document is there, fetch its id and close |
| 429 / rate limit | The receiving system is asking you to slow down | Retry respecting the indicated interval, with the queue acting as the brake |
| 5xx / timeout | The system is temporarily unavailable | Retry with increasing intervals, a maximum number of attempts, then error queue with an alert |
| No response at all | You do not know whether the operation went through | Retry with the same idempotency key: this is exactly the case it exists for |
The most important distinction is between an error that retrying will fix and an error that needs a person. A middleware that retries an order with the wrong VAT number forever burns resources and hides the problem; one that discards on the first network error loses orders. Separating the two queues is the design decision that cuts support calls the most.
The quote and the timeline of an integration depend less on the number of flows and more on what the system at the other end exposes. This is the casuistry we meet most often, from the simplest to the most demanding.
| Type of system | Typical interface | The critical point | Relative effort |
|---|---|---|---|
| Modern e-commerce or CMS (headless, SaaS) | Documented REST APIs and webhooks | Call rate limits, and webhooks that can be delivered twice | Low |
| Recent cloud ERP | REST APIs, often metered | The product data model does not match yours: it needs mapping, not just transport | Low to medium |
| Widely used on-premise management system | Import/export modules, staging tables, sometimes a web service | Writing straight into the tables bypasses the application's own rules: go through its procedures | Medium |
| Legacy system with no APIs (e.g. AS400) | Data access, callable programs, structured files | Needs a dedicated layer translating into clean APIs without touching the management system | Medium to high |
| Carriers and hauliers | APIs for labels and tracking | Each has its own rules on parcels, cash on delivery and returns: one adapter per carrier is worth it | Low, but multiplied |
| Marketplaces and customer portals | Proprietary APIs or EDI | The counterparty sets the rules and changes them without notice: versioning and monitoring are mandatory | Variable |
The question to put to your ERP vendor, before any quote, is a single one: which interfaces do you provide, with what documentation and with what limits. The answer moves the cost more than any other variable. If the answer is «none», that is not a dead end: it is the Conor case, and a dedicated layer solves it.
An integration is not «done» when the first order goes through: it is done when the company has a way of noticing by itself that orders have stopped going through. These are the indicators we ask to be able to read on a dashboard, and that we suggest demanding from whoever builds the integration.
How long it takes for an event in the source system to appear in the destination system. It should be measured on the real-time flows (order, stock, price list) and compared against the agreed threshold: if the figure degrades over time, the bottleneck is usually the receiving system, not the integration.
The absolute number matters less than its trend. A queue that grows every day and that nobody empties means the company has already stopped trusting the integration and gone back to re-keying by hand.
A periodic comparison of totals across systems (orders of the day, lines, amounts) that flags differences. It is the check that catches silent errors: the ones where no call failed, but a record arrived incomplete.
The only indicator that matters to whoever signed off the investment. It has to be estimated before starting, by counting how many times a day a piece of data is re-typed and by how many people, and measured again afterwards: the same logic as the incremental method for digitalising an SME, where no release starts without a number agreed up front.
Codebaker is a software house based in Bologna, founded in 2019, that builds custom software, apps, APIs and AI integrations in-house. The source code stays the customer's property.
What it is based on
You integrate them by building a layer of custom APIs — a middleware — that acts as the meeting point between the systems instead of wiring them together in pairs. The path has five moves: first, decide for each entity (customer, item, price list, stock, order) which system holds the truth, so there are never two versions of the same data; second, write the data contract, meaning which fields travel, in which format and under which validation rules; third, develop the middleware that exposes REST or GraphQL APIs, normalises the formats and propagates events via webhooks; fourth, choose for each flow whether real time is genuinely needed or a periodic batch is enough; fifth, add queues, retries, idempotency and logs, because a serious integration is not one that works, it is one that notices when it stops working. The typical result is the chain e-commerce → API middleware → management system/ERP → warehouse → courier, with a single piece of data travelling through every system without anyone retyping it.
There are six recurring entities. From the management system towards the e-commerce travel customer master data with credit limits and terms, the item catalogue with descriptions and attributes, price lists (often customer-specific in B2B) and warehouse stock. From the e-commerce towards the management system travel the orders and, downstream, delivery notes and invoices come back for the customer to consult. The sixth entity is the delivery status, which arrives from the courier and must be visible both in the management system and in the customer's private area. In B2B, stock levels and customer-specific price lists are usually the flows that genuinely require real time, because a wrong price or an out-of-date availability generates an order that has to be cancelled.
Yes, and it is one of the cases we handle most often. When the management system exposes no modern APIs, we build a dedicated integration layer that talks to it in the ways it does support — direct data access, native procedures, structured files, services exposed by the middleware — and translates all of it outward into clean REST APIs. The management system is neither modified nor replaced: it keeps working exactly as before, while the rest of the world talks to the layer. That is precisely what we did at Conor, where a B2B e-commerce with native iOS and Android apps and a web version synchronises in real time with an AS400 system, handling more than 300,000 orders a year and 5,000 customers a day.
It depends on how standard your flows are. File exchange (CSV or EDI over FTP) is cheap and still holds up well for batch flows towards external partners, but gives you neither real time nor fine-grained error handling. Standard connectors and iPaaS platforms work well when both systems are widely used products and the fields line up, but they become expensive and brittle as soon as unusual transformations are needed; they also introduce a subscription and a dependency on the platform vendor. Custom APIs have a higher initial cost, but they cover any business rule, remain your property and are the only route when one of the systems is legacy or when the integration logic is itself part of your competitive advantage. In practice many companies use a mix: custom APIs for the critical flows, batch files for the marginal ones.
No, and demanding it is one of the most common ways to inflate the cost of an integration with no benefit. Real time is needed where stale data produces an immediate commercial error: stock levels, customer-specific price lists, credit limits, order status. For full master data, history, statistics and accounting reconciliation, a periodic alignment is more than enough, and it is also more robust because it tolerates a system being temporarily unavailable. The rule of thumb we apply is: real time on whatever blocks a sale, batch on everything else.
A bounded integration on a few flows towards a system that already exposes documented APIs typically falls in the €5,000-15,000 band; a complete integration layer between e-commerce, management system, warehouse and couriers, with error handling and reconciliation, falls in the €15,000-50,000 band; architectures spanning several systems, several sites or high volumes go beyond €50,000. The variable that moves the price most is not the number of entities but the quality of the available interfaces: connecting to an ERP with documented REST APIs costs a fraction of building a layer towards a system that has none.
Five, in our experience. First: not deciding which system holds the truth for each entity, and ending up with two customer records that drift apart. Second: treating integration as the last task of the project rather than an architectural requirement, only to discover at acceptance testing that an indispensable field does not exist. Third: ignoring the edge cases — partial orders, returns, changes after confirmation, blocked customers — which are the majority of the real work. Fourth: providing no error handling, retries or idempotency, so a lost webhook becomes a lost order and nobody notices for days. Fifth: not monitoring the flows in production, which is like having a fire alarm with no siren.
With three standard mechanisms we design in from the start. Idempotency: every message carries a unique identifier, so if it is sent twice the receiving system recognises it and does not create a second order. Queues with retries: if the management system is momentarily unreachable, the message is not discarded but retried at increasing intervals until it succeeds. Reconciliation: a periodic check compares totals across systems and flags discrepancies. On top of these sits monitoring with alerting, because an interrupted flow must be discovered by the system, not by the customer phoning in.
The client. The integration layer, the code and the documentation belong to the company, built on standard, open technologies. This is a substantial difference from subscription connectors: if tomorrow you change e-commerce platform or want to connect a second sales channel, the middleware is already there and it is yours, and the APIs talking to the management system do not have to be rebuilt. It is also why an integration investment, done well, amortises across the next project instead of starting from zero.
Idempotency is the property whereby sending the same message twice has the same effect as sending it once. In practice every order carries a unique key (for example an Idempotency-Key header holding the source order reference): if the network drops after the request is sent but before the response arrives, the sender does not know whether the operation went through and retries; the receiving system recognises the key, understands the document already exists and returns its identifier instead of creating a second one. It is the only way to tell «it never arrived» apart from «I never got the confirmation», and without it every retry is a potential duplicate order. It is the first question to ask anyone proposing an integration.
With three measures. First, deduplication: every event carries an identifier and the middleware keeps track of the ones already processed, so a repeated delivery is discarded. Second, the event sequence number or timestamp: if an status update arrives that is older than the one already recorded, it is ignored rather than overwriting more recent information — the classic case of «in transit» arriving after «delivered». Third, periodic reconciliation: a check that realigns states against the source, because a webhook can also simply never arrive and no event system should be treated as 100% reliable. Designing for the lost webhook is cheaper than chasing it afterwards.
Not if the middleware is designed to absorb load rather than pass it on. The two mechanisms that prevent it are the queue and the cache. The queue decouples timing: the e-commerce immediately receives confirmation that the order has been accepted, while the write into the management system happens as soon as that system is ready, without the customer waiting. The cache is for high-frequency reads, such as stock levels queried by hundreds of users a minute: the middleware answers from its own up-to-date copy instead of hitting the ERP on every product view. This is exactly why the intermediate layer exists: without it the management system, often the least scalable component in the chain, becomes the bottleneck of the e-commerce.
Codebaker is a software house based in Bologna, at Via N. Corazza 7/8, founded in 2019, that builds integrations between management systems, ERPs, e-commerce platforms, CRMs and legacy systems in-house. Integrations in production include ConorShop, a B2B e-commerce with native iOS and Android apps synchronised in real time with an AS400 management system (5,000+ customers a day, 300,000+ orders a year), and Granarolo's IAM system integrated with SAP, HR, Active Directory and Office 365 (2,000+ users, -95% IT onboarding activity). The code and the documentation stay the customer's property, the technologies are standard and open, there are no per-user subscription fees and the cost brackets are published on the site. The preliminary analysis and the quote are free: it starts from a map of the existing systems and of the points where someone is still re-keying data by hand.
Tell us which systems you run today — management software, e-commerce, CRM, warehouse, couriers — and where somebody is still retyping data by hand. We will map the flows, tell you what genuinely needs real time and which integration is worth doing first. The preliminary analysis and the quote are free.