Engineering - Case Study

Building a Customer Portal on an ERP API That Doesn't Do Everything You Need

What we learned putting a customer portal in front of the ERP.Aero API for LGM Aviation: map the gaps before you design, measure before you cache, fail safe on undocumented behaviour, and take the cheapest honest route to data the ERP can't give you.

Share
Building a Customer Portal on an ERP API That Doesn't Do Everything You Need - Tec Dynamics

In short

LGM Aviation supplies aircraft parts, and its customers wanted to see their orders, documents and status without emailing someone to ask. We built them a customer portal and admin panel on top of their ERP, ERP.Aero, and went live with version 1.0 in July 2026. Most of the interesting engineering wasn't in the portal. It was in dealing honestly with what the ERP's API could and couldn't do. What the portal does for LGM is in the LGM Aviation case study; this post is about the API work underneath it.

Map the gaps before you design anything

The project started in March 2026. Before designing screens we went through the API against every feature in the plan and wrote a gap report in April. At the time the API (v2.7.9) came as a Postman collection only, with no OpenAPI spec, so the only way to know what worked was to call it.

Every feature got a line in a coverage matrix: available, partial or missing. Missing included a fleet list and aircraft fields, a link between sales orders and aircraft, team notes, webhooks, text search and contact updates. Partial or ambiguous included mapping a login email to a company, the enum for AOG (aircraft on ground) priority, document filtering and the order stage values.

That report changed the plan. Admin work planned for sprint 3 and AI work planned for sprint 5 were pulled into sprint 2, because the shape of the ERP data decided how both had to be built. That is the whole value of doing the exercise first.

Measure before you cache

The ERP was slow for the kind of access a portal needs. On the sandbox, the order list took tens of seconds per page and timed out under any concurrency. On live, the cheapest call we could find, a picklist lookup, took 7.1 seconds. One dashboard query measured 45 seconds per page, and above roughly 10 concurrent calls the ERP started returning 504s.

We designed around those numbers rather than around the documentation. For each customer the portal does one full scroll of their orders, then merges a page-1 delta by order id at most once a minute, and re-scrolls fully every 30 minutes to catch deletions. The cache warms when the customer logs in. Order detail is cached for five minutes and shared with the AI assistant, so a question to the assistant doesn't trigger its own round of ERP calls. Admin reads use stale-while-revalidate, the dashboard never waits on a full scroll, and concurrent ERP calls are capped at 5.

login            -> warm this customer's order cache (full scroll, background)
every request    -> serve from cache
at most 1/min    -> fetch page 1, merge by so_id          (new and changed orders)
every 30 min     -> full re-scroll                        (catch deletions)
ERP concurrency  -> max 5 calls                           (504s above ~10)

Undocumented behaviour: make it safe by construction

The most dangerous API behaviour is the kind that fails quietly. In July we found that date filters on the order list were silently ignored: every date operator returned the same 1,256 orders, while a nonsense status value correctly returned none. A "last six months" view built on that filter would have shown everything and looked fine.

So the portal doesn't trust it. It pages newest-first and stops when it passes the cutoff. Deltas rely on sort order, not on the modified-time filter.

The same rule applies to shortcuts. One customer has more than 1,000 orders, and finding an order by number used to mean walking 25+ pages. An undocumented filter turned that into one request. The portal uses it only when it returns an exact match; any error, miss or sign that the filter was ignored falls back to the full scan, and an environment flag switches the shortcut off entirely. Fast when it works, correct when it doesn't.

Documents, statuses and priorities

Documents couldn't be listed per customer, because the document list endpoint has no customer filter and the sandbox held around 13,000 documents. Instead the portal walks each order's details. Those arrive in three different shapes, one of which is a relative link to the ERP's own screens rather than a downloadable file, and some document ids turn up without their record. Those are fetched one at a time and cached for ten minutes.

Status values drifted too. The ERP returned values beyond the four in the documentation, and LGM prefixes some of its own, so "Cancelled" was briefly being counted as open. The portal now keeps a status registry: new values are imported from the ERP's picklist, stay hidden until LGM staff review them, and are never deleted.

The AI assistant hit a version of the same trap. Asked "how many AOG orders do we have?", it answered zero, because it was reading only the latest 25 orders and matching on status, and AOG is a priority, not a status. It now scans the full cached set and matches on either.

Data the ERP can't give you

The hardest gap was aircraft. Customers wanted to see which aircraft an order was for, and the API had no fleet data or link between orders and aircraft. The vendor confirmed in June that the data exists in the ERP but would need paid development to expose. We put priced options side by side: an ERP-side build quoted at 8 to 10 weeks, or a portal-side fleet module of about two weeks.

What actually shipped was smaller than either, and we think it was the right call. Many customers already put the aircraft serial number (MSN) in their purchase order reference, so the portal reads it from there. It only accepts a value introduced by "MSN", and leaves the cell empty rather than guess. That caution came from experience: an earlier stand-in field had put the word EMAIL in every row, because it turned out to hold the channel the order came in through, not an aircraft. A proper fleet module is still an option for later.

Some data simply doesn't belong in the ERP. Team notes live in the portal's own database, and contact changes deep-link into the ERP rather than being edited from the portal, so the ERP stays the single source for anything it owns.

If you're about to do the same

  • Write the gap matrix before the design. Every "missing" line is either a workaround, a vendor request or a feature you drop, and the client should choose which.
  • Measure the real API on live data before choosing a caching strategy. Documentation rarely states latency.
  • Treat every undocumented filter as untrusted: use it only when its result can be checked, and fall back to the slow correct path.
  • Prefer an empty cell to a wrong one. Nobody complains about a blank MSN; everyone complains about the wrong aircraft.
  • Keep a registry for enum values you don't control. They will change without notice.

We take on this kind of work as technology consultancy before a line of code is written, and as customer portal development after. Talk to us about your ERP.

Considering a similar project?

Tell us about your stack and what you are trying to integrate. We reply within 2 business days with a clear scope and indicative pricing.

Get a Free Consultation ← Back to the Blog