Syncing POS & EC Transactions to CRM Deals

Unifying store and EC sales as HubSpot Deals — the full purchase picture per customer in your CRM

HubSpotDealTransaction SyncPOSEC
6 min read

Introduction

"When, where, and what did this customer buy?" — can you see that at a glance?
When your in-store POS and your EC order management live in separate systems, this is surprisingly hard to see.

For an apparel and gear EC business I worked on, I built a pipeline that syncs transaction data from a store POS (Smaregi) and an EC order manager (NextEngine) into the CRM (HubSpot) as Deals.
This article walks through the overall picture and the thinking behind the design.

Why hold sales as "Deal objects"

The customer 360 view

A CRM organizes everything around the customer (Contact). If you attach sales to a Contact as Deals, then just opening a customer's page shows you everything they have ever bought.

Rather than keeping sales as a flat aggregate table, you hold them as a set of transactions tied to each customer. That is the foundation of what people call the customer 360 view. Marketers can hold conversations with full purchase context, without any special effort.

LTV across store and EC

Some people buy in store, some buy online, and many do both — real customers move freely across channels. But when systems are split, you can never see a person's true total spend.

Consolidating transactions into one CRM lets you finally calculate cross-channel LTV (lifetime value). You can properly value a customer who looks small on EC alone but is a regular in-store. That really pays off when deciding where to focus your efforts.

A foundation for cross-sell

When purchase history is laid out as Deals and line items, you can build cross-sell conditions like "recommend B to people who bought A" directly with the CRM's segmentation tools.

For example, you could pull "people who bought a jacket last season but haven't bought gloves yet" and email them. Because sales data is structured as transaction objects, this kind of filtering becomes practical to run with reasonable effort.

What gets synced — the big picture

Breaking one transaction into three objects

A single transaction is broken into three kinds of objects in the CRM. The transaction itself becomes a Deal, the buyer becomes a Contact, and each purchased product becomes a LineItem.

By relating these three correctly, "who bought what, for how much" is preserved in a structured form. The detailed design of this breakdown is covered in the mapping sub-article.

The flow of data

The sync fetches transactions from POS/EC, transforms them, and writes them to the CRM. Let's lay out the order of processing to grasp the whole.

End-to-end sync flow
Fetch transactions

Pull transaction data for the target period from Smaregi and NextEngine

Match the buyer

Look up a Contact by email or customer number, create one if none exists

Convert to Deal & LineItem

Map the transaction to a Deal and product rows to LineItems

Write to the CRM

Associate the Deal with the Contact and attach LineItems to the Deal

We verify this pipeline safely with the Dry Run described later, then let it run for real.

The difficulty of identity matching

Matching on email and customer number

The most nerve-racking part of the sync is "identity matching" — correctly tying a buyer to an existing Contact. If the same person registered separately in-store and online, or changed their email, you can easily create them as a different person.

In this project, I use email as the primary key while also using the membership number (customer number) as a secondary key. Relying on either one alone leads to misses, so matching against multiple keys turned out to be the pragmatic answer.

Preventing duplicates

If matching fails and Contacts get duplicated, your hard-won customer 360 view gets fragmented. If store transactions hang off one Contact and EC transactions off another, LTV won't come out right either.

Mechanisms for safe operation

The reassurance of Dry Run

Writing straight to a production CRM is scary. The first sync in particular has a large volume, and a mapping mistake could create a flood of wrong Deals.

So I built a Dry Run mode that writes nothing and only reports "what would be created or updated." You review the results, confirm there are no issues, and then switch to the live sync. Just having this "chance to pause" makes operating the system feel completely different.

Daily sync and manual runs

For steady-state operation, a daily automatic sync runs via QStash (a scheduler), pulling in the previous day's transactions in a batch. On top of that, you can trigger a manual run from the admin screen at any time.

Running daily keeps the CRM always up to date, and manual runs let you flexibly handle situations like "I want to fill a gap right now" or "I need to re-sync a specific period."

Conclusion

The aim of syncing POS and EC transactions into CRM Deals is to make each customer's cross-channel purchase picture visible in one place. Structuring sales as transaction objects builds the foundation for LTV evaluation and cross-sell campaigns.

Three sub-articles dig deeper into this theme.