← Back to client work
Swedish B2B company · HubSpot, Litium and Business Central · 2026

Enter a customer once, in HubSpot. The portal and the ERP follow.

A Swedish B2B company runs its customer portal on Litium and its finance on Microsoft Business Central. HubSpot was meant to be where customers start, but nothing connected it to either. Now companies and contacts travel from HubSpot to the portal, and through it to the ERP, without anyone typing them again. Changes made in the portal come back to HubSpot with who changed what, and when.

Entered once, in HubSpotA seller adds the company and its contacts where they already work.
HubSpotwhere customers start
CompanyExample Trading AB · Partner
Note on the timelineBilling email changed via the portal, by Anna Berg
Edited in HubSpotLanguage set to Swedish
Echo recognisedIts own change came back. Nothing is sent again.
B2B portalLitium
Customer createdExample Trading AB, with its contacts
Changed by a portal userBilling email updated
Updated from HubSpotLanguage: Swedish
ERPBusiness Central
Customer accountReceived from the portal, nothing typed
1

place a customer is entered. The portal and the ERP receive it from HubSpot, so nobody types the same company twice.

2

directions kept in step. Every change travels once and stops, whichever side it was made on.

3

detailed validation sessions in one week, after which the client approved the integration in full.

Signed off  by the client in May 2026, then released to full integrated testing.

01What changed

Five things the sales team gets that did not exist before.

Same portal, same ERP, same sellers. What changed is where a customer starts, and how much anyone has to type or chase afterwards.

Entry

One place to start

Companies and contacts are created in HubSpot. The portal and the ERP receive them from there.

Effort

No retyping

Contacts created in the portal arrive in HubSpot already linked to their company, with its status.

Control

A person approves the first send

A new customer reaches the portal from a button, after a check. From then on, changes flow by themselves.

Risk

Bad data stops at the door

An unknown seller code, language or country gets a clear message in HubSpot instead of reaching the ERP.

Audit

Every change has an author

Each field changed in the portal becomes a note in HubSpot: what, from, to, when, and by whom.

02See it work

The seller never leaves the company record.

The real card, rebuilt in the page and running on a loop. It appears when the company becomes a partner, lists what is still missing, and refreshes on its own while the seller fills it in. Touch it to step through yourself.

A company that turns into a partner gets a button to create it in the portal. The button stays locked until every field the portal needs is filled, and the card names the ones still empty. If the portal refuses a value, the seller reads why, in plain words, in the same card. Once the company is active in the portal, the button goes away and every later change syncs on its own.

Clicks to create a customer in the portal
1
0 reloadsthe card refreshes itself every three seconds
Portal sync · inside the company Refreshing
01 Company · Example Trading AB
Lifecycle stageCustomer In the portalNot created
Required by the portal
Registration number556123-4567
LanguageSwedish
Company billing emailempty
Seller codeempty
Create in the portal
The button appears when the company becomes a partner.
When the button shows, and when it does nottwo conditions and a list of what is missing

The button appears only when the company's lifecycle stage is Partner and the company is not yet active in the portal. Before it can be pressed, the card lists every required field that is still empty. It refreshes every three seconds, so the seller sees the change as soon as a field is saved, and a refresh that lands in the middle of a click cannot switch the button back on and send the company twice.

Why the first send is manualand everything after it is not

Creating a customer in the portal also creates it in the ERP, where it can be invoiced. That is a commitment, so a person makes it, after the card confirms nothing is missing. Once the company is active, any change on either side syncs automatically. The judgement sits where it matters and nowhere else.

03Two-way, without the echo

Two systems that listen to each other will talk forever.

HubSpot tells the portal about a change. The portal saves it, and tells HubSpot about the change it just saved. Without a guard, that never ends. It alternates by itself; pick a side to hold it.

HubSpotchange made here
B2B portalsaves and reports back
0 updates sent for one change of language.
Three ways to stop it, and the one chosenfingerprint, time lock, origin flag

A time lock ignores everything for a while after a send, and loses real changes made in that window. An origin flag needs both systems to carry and respect a marker, which depends on the other vendor. The fingerprint needs neither: the service remembers what it just sent, and when the same data comes back within fifteen seconds, it recognises its own echo and stops.

Building it turned up the detail that makes or breaks this approach: the same identifier arrived as text from one side and as a number from the other, so two identical changes looked different. Every value is normalised before the fingerprint is taken.

04What comes back

Nobody has to ask who changed the billing email.

Every property changed in the portal is written to the company's timeline in HubSpot, with the old value, the new one, the Stockholm time and the person who did it. The timeline on the right fills itself on a loop.

One note carries
FieldOld valueNew valueDate and timeStockholm time zoneVia the portalWho did it

The portal sends the id of the user who made the change, and the integration finds that person among the company's contacts in HubSpot. If it cannot, the change still goes through: the note says "Unknown user", and the portal gets a warning it can pass to its administrator.

A value the portal refuses is handled the same way. If a seller types an unknown seller code in HubSpot, the field goes back to the last valid value and a note says why, so the wrong value never keeps bouncing between the systems.

Company timeline · HubSpot Live
↺
12 Jun 2026 · 14:32 StockholmVia the portal

Billing email changed from invoice@example.se to billing@example.se, by Anna Berg.

↺
12 Jun 2026 · 14:33 StockholmVia the portal

Language changed from English to Swedish, by Anna Berg.

+
12 Jun 2026 · 15:02 StockholmVia the portal

New contact Erik Lund created in the portal, already linked to this company and its status.

?
13 Jun 2026 · 09:10 StockholmAdmin alerted

Phone changed from 031 00 00 00 to 031 11 11 11, by Unknown user. The change was kept.

!
13 Jun 2026 · 10:47 StockholmReverted

Seller code XY9 was not accepted by the portal. The field went back to AB1.

Illustrative dataWritten by the integration
Portal forms counted as the wrong contacta tracking side effect found along the way

HubSpot's tracking code was also reading forms inside the portal, and attributing those submissions to whichever contact the browser remembered, not the one the form was about. The recommendation was to block the tracking on the portal's internal pages through the tag manager, so marketing reports only count real submissions.

05What could break it

Six ways a two-way sync goes wrong, and what stops each one.

None of these show up in a demo. They show up in the third month. They resolve one at a time; click any of them to drive it yourself.

Open riskHandled

Endless loop

Each system reports the other's change back as new, forever.

A fingerprint of what was sent, kept for fifteen seconds, with every value normalised.

Open riskHandled

Bad data in the ERP

An unknown seller code, language or country travels on and breaks something downstream.

The portal's refusal becomes a clear message in the card, and a bad edit in HubSpot is reverted with a note.

Open riskHandled

Duplicate customers

The same company created twice, because two systems disagree on what makes it the same.

The key was settled before building: HubSpot id, registration number and email weighed, including branches that share one registration number.

Open riskHandled

Silent errors

A validation failure turned into a generic server error, and the seller sees nothing useful.

The real reason reaches the card, word for word.

Open riskHandled

A contract that moves

Field names that differ between the portal's documentation and its behaviour, wrong methods, a delete that arrives empty.

Each one traced from the logs and agreed with the portal's developer, with the API documentation versioned up to V6.

Open riskHandled

Changes with no author

A field changes and nobody can say who did it.

The portal user is resolved to a HubSpot contact. If that fails, the change is kept and the administrator is warned.

0 of 6 failure modes handled before sign-off, on a portal owned and built by another vendor.
The July incidenta card answering with a web page

In July the card started receiving a web page where it expected data. Checking layer by layer, from the card to the service behind it, showed a web server setting that clashed with another integration hosted on the same server. The two configurations were merged into one, and neither service went down while it was fixed.

06What it leaves behind

Every answer has a name, and every rule is written down.

The portal is built by another agency. For them to react to each case on their own, every answer from the integration carries a code a program can read, and every rule lives in a document rather than in someone's head.

One answer from the integration
{
  "code": "company_updated",
  "message": "Company updated in HubSpot",
  "modified_by": "Anna Berg"
}

The portal team reads the code, not the sentence, so each case can be handled automatically: updated, rejected, or accepted with a warning for the administrator.

For the portal's developers
  • A plain-language guide to the portal's API
  • A reference of every endpoint the integration exposes
  • API documentation versioned up to V6, June 2026
For whoever maintains it
  • A full technical guide to the integration
  • Flowcharts for creating a company and validating a contact
For sign-off
  • A test checklist covering every scenario, with the expected status and answer
  • A walkthrough video of every feature, recorded for the client
What it runs onsmall, owned, and documented

A Python service built with FastAPI on a dedicated server, behind Nginx with SSL, run as a system service and versioned in a private repository. It is split into four layers: authentication with the portal (OAuth 2.0, with the token cached and renewed ahead of time), the endpoints that receive changes from each side, the clients that talk to HubSpot and to the portal, and the mapping that translates one system's fields into the other's. Webhooks from HubSpot are verified by signature, and those from the portal by a shared secret.

In HubSpot: custom properties for customer number, registration number, language, seller code, company billing email and the contact's role in the portal, plus sync status fields, and the card itself, built as a HubSpot UI extension in React and TypeScript.

07What we take from it

An integration is judged by what the seller sees when something is wrong.

Moving data between two systems is the easy half. The half that decides whether people trust it is the message on the screen when the portal says no, the note that says who changed a field, and the change that travels once and stops.

Your systems

Make HubSpot the front door to your portal and your ERP.

  • Two-way sync, with a person approving the first send and everything after it automatic
  • Every change written back to HubSpot with its author and time
  • Built to hold up against an API owned by another vendor

Thirty minutes, your systems on the screen, no slides.

Book 30 minutes with Nils (opens in a new tab)