HubSpot ↔ Acumatica Sync Guide
Bi-directional data synchronization between HubSpot CRM and Acumatica ERP.
Architecture
Acumatica (ERP)
↕ REST API (15-min cron)
webhook-router (BullMQ workers)
↕ REST API + Webhooks
HubSpot (CRM)
All sync runs through the webhook-router service on Railway, using BullMQ queues backed by Redis.
Sync Paths
| Path | Direction | Schedule | Dedup Key |
|---|---|---|---|
| Customers → Companies | Acumatica → HubSpot | 15-min cron | acumatica_customer_id |
| Customers → Contacts | Acumatica → HubSpot | 15-min cron | email (quality gated) |
| StockItems → Products | Acumatica → HubSpot | 15-min cron | acumatica_inventory_id |
| SalesOrders → Orders | Acumatica → HubSpot | 15-min cron | hs_external_order_id |
| Order Details → Line Items | Acumatica → HubSpot | 15-min cron | acumatica_line_key |
| Cases → Tickets | Bi-directional | 10-min cron + webhook | acumatica_case_id |
Sync Phases
Each sync cycle follows a 3-phase pattern:
- Phase 1: Fetch — Query Acumatica entities modified since last sync (session gate coordinated)
- Phase 2: Push — Upsert records to HubSpot (no Acumatica session needed)
- Phase 3: Write-Back — Write HubSpot record IDs back to Acumatica attributes (session gate coordinated)
Company Properties Synced
| HubSpot Property | Acumatica Source |
|---|---|
name | CustomerName |
phone | MainContact.Phone1 |
address, city, state, zip, country | MainContact.Address.* |
acumatica_customer_id | CustomerID |
acumatica_credit_limit | CreditLimit |
acumatica_payment_terms | Terms |
acumatica_account_balance | CreditVerificationRules balance |
acumatica_customer_class | CustomerClass |
acumatica_salesperson | Salespersons[].Default |
acumatica_ship_via | ShipVia |
Write-Back (HubSpot → Acumatica)
After successful HubSpot upserts, the sync writes HubSpot record IDs back to Acumatica:
| Acumatica Attribute | HubSpot Source | Method |
|---|---|---|
HUBSPOTPID (StockItem) | Product ID | Attribute write |
HUBSPOTCID (Customer) | Company ID | Attribute write |
UsrHubSpotDealId (SalesOrder) | Order ID | DAC UDF write |
HUBSPOTTID (Case) | Ticket ID | Attribute write |
Echo Prevention
For bi-directional sync (Cases ↔ Tickets), Redis lock keys prevent echo loops:
case-sync:lock:{caseCD}— 120s TTL after Acumatica → HubSpot synccase-sync:lock:ticket:{ticketId}— 120s TTL after HubSpot → Acumatica sync
Transform Functions
Key transform functions in acumatica-transform.ts:
val(field)— Unwraps{value: x}→ raw valuestr(field)— Unwraps to stringtoDateStr(field)— Converts to epoch ms string for HubSpotextractBalance(record)— Reads fromCreditVerificationRuleswith fallbackextractDefaultSalesperson(record)— Finds default fromSalespersons[]array
Debugging
Check sync status
GET /sync/status
Force a sync cycle
POST /sync/trigger
View sync logs
Check Railway logs for the webhook-router service. Filter by [sync] prefix.