- SO-4812Shipped
- SO-4813Approved
- SO-4814Cancelled
- wholesale only✓
- retailer matched✓
- no duplicate✓
- SKUs linked✓
A brand sells to just over 600 retailers. Orders come in through the back office, NuOrder, marketplaces and dropship partners. All of them end up in Cin7 Core. Sales works in HubSpot, and there it sees none of those orders. Which retailer ordered last, and when? Who used to reorder every season and has been quiet for six months? The answer sits in the ERP, and nobody in sales opens the ERP.
That was the situation at a North American jewellery brand selling wholesale, for whom we built the connection. The client stays unnamed here. The pattern is the same in any wholesale business running Cin7 and HubSpot.
The short version
- The native Cin7 integration for HubSpot syncs products and contacts. It does not transfer retailers as companies, orders or order lines.
- We built a separate sync next to it that writes in one direction only: from Cin7 to HubSpot. The native integration stays on.
- From the last order, the sync calculates a status for every retailer. When a retailer slips, the team gets a task.
What the native integration transfers
Cin7 thinks in customers, sales orders and stock items. HubSpot thinks in companies, contacts and deals. The native app translates the catalogue and the people. The part sales actually needs is missing: which retailer ordered what, when they last ordered, through which channel and on which terms.
How we approached it
Before writing a line of code, we settled three things.
One direction. Cin7 stays the source of truth for orders, prices and stock. HubSpot only reads. Nothing in the CRM writes back to the ERP. That keeps the room for errors small and the accounting team relaxed.
One fixed key. Every retailer in HubSpot carries its Cin7 ID, every deal its order number. That is how the sync finds the right record again on every run, without relying on names or email domains.
Wholesale only. Orders from the consumer web shops, suppliers, test accounts and cancelled orders are filtered out before anything gets written.
A single run then looks like this:
- Load changed orderssince the last run, with a 7-day buffer
- Filterwholesale channels only, no cancellations, no test accounts
- Match the retailerby Cin7 ID, with price tier, payment terms, credit limit, sales rep
- Order becomes a dealduplicate check via the company's deals, not via search
- Line items and contactslinked to the native products by SKU, buyers on company and deal
- Recalculate the statusfrom the last real order, task when a retailer slips
The sync is a small Python program that runs on a schedule in GitHub Actions. Each run fetches new and changed orders since the last run, with a generous buffer so a failed run loses nothing. And every run can start as a dry run first, which only logs what it would write.
From the last order to an account status
The real work for sales starts once the data is there. From a retailer’s last real order, the sync calculates a status in fixed bands on every run:
- Activeunder 75 days
- Needs attention75 to 119
- At risk120 to 179
- Lapsed180 to 364
- Dormant365+ or never
An open order that has not shipped yet lifts “needs attention” and “at risk” back to “active”. Someone who just ordered should not get a call only because their last delivery was three months ago. When a retailer drops a band, the sync creates a task for the retention team. Only on an actual drop, not on every recalculation.
In HubSpot, saved views sit on top of this: retailers at risk, retailers that need attention, retailers to win back. And one for the most valuable accounts that are slipping right now.
What to watch out for if you build this yourself
- Don’t check for duplicates through search. HubSpot search is not updated instantly. Search for a deal right after creating it and you sometimes won’t find it yet, and create it a second time. The reliable route goes through the company: read its associated deals and look for the order number there.
- Remove cancellations actively. Cin7 keeps returning cancelled orders unless you filter for them explicitly. Skip that step and HubSpot shows revenue that never existed. In this project it added up to a six-figure amount.
- Check workflows before the backfill. A workflow that creates something for every won deal fires 4,000 times when you load 4,000 historical orders. Load first, then switch it on, or guard the workflow so it only reacts to genuinely new deals.
- Exclude auto-created companies. HubSpot can create a company from every email domain it sees. In this portal that was around 40 percent of all companies, HubSpot itself included. Every count, view and list filters on the Cin7 ID.
- Start with a handful of real customers. Five real retailers with all their orders, line items and contacts show more edge cases than any test record.
The result
After the first backfill, twelve months of wholesale business sat in HubSpot: around 4,400 orders as deals, 26,000 line items linked to products, just over 900 contacts, not a single duplicate. Since then the sync keeps everything current on its own. Sales sees on every retailer what they ordered, on which terms, and whether they are slipping. Nobody needs to open the ERP for that anymore.
The project at a glance is in our case studies.
When it is worth it
With a few dozen retailers, a look into the ERP is enough. From a few hundred retailers, several order channels and a team that is supposed to drive reorders actively, the sync pays off. Especially when HubSpot is already in place and only the data is missing.
An integration like this one is one step in a larger project. How we run the whole project, from deciding every field to training the team, is in HubSpot implementation: how it works, step by step.