Decide the system of record per field, not per system
It is rarely true that one system owns everything. Billing owns invoice status. HubSpot owns lifecycle stage. Your product database owns usage. Write it down as a field-level table before building anything.
| Data | Typical owner | Sync direction into HubSpot |
|---|---|---|
| Invoice and payment status | Billing / ERP | One-way in, read-only in HubSpot |
| Product usage and entitlements | Product database | One-way in |
| Lifecycle stage and owner | HubSpot | One-way out, if anything |
| Support ticket status | Helpdesk (or HubSpot if it is the helpdesk) | One-way from whichever owns it |
| Company firmographics | Enrichment provider | One-way in, with manual override flagged |
The unique key is the whole game
- Match companies on a stable external ID, or failing that on root domain — never on company name.
- Match contacts on email, and decide in advance what happens when email changes.
- Store the external system's record ID in a dedicated HubSpot property, and never let a human edit it.
- Set the property to a type that won't be reformatted — long numeric IDs turned into scientific notation is a real and common failure.
- Test the key against a sample containing your messiest records, not your cleanest.
Two-way sync is a promise you have to keep
Two-way sync means you have accepted that either side can overwrite the other, and you need conflict rules: last write wins, or one side is authoritative on conflict. If you can't state the rule, you don't want two-way sync on that field.
The recurring damage from unstated conflict rules is silent overwrite loops — two systems flipping a value back and forth, each triggering the other's automation, burning API calls and firing workflows.
Rate limits, batching and volume
API call limits are edition-dependent and easy to exhaust with per-record updates. Batch where the API supports it, sync on change rather than on schedule where you can, and exclude fields nobody reads.
A useful discipline: for every field you are about to sync, name the report or workflow that consumes it. Fields with no consumer should not be synced at all.
Failure handling is the part everyone skips
- 01Decide who receives sync failure alerts, by name, not 'the ops inbox'.
- 02Log failures somewhere reviewable — an error property, a list, or the integration's own log with a weekly check in someone's calendar.
- 03Add a data-quality dashboard tile for records that failed to sync in the last seven days.
- 04Document the manual replay procedure before you need it at 6pm on a Friday.
- 05Re-test the integration after any HubSpot property change; renaming or changing a field type is the most common cause of a break.
Native, iPaaS or custom
Use the native or marketplace connector when it covers your fields — it is maintained by someone else, which is worth a great deal. Move to an iPaaS platform when you need transformation logic or several systems in a chain. Build custom only when volume, timing or logic genuinely exceed both, and accept that you now own maintenance forever.
Custom integrations built by a departed contractor with no documentation are among the most expensive things we find in audits.
Common mistakes
- Matching companies on name and creating thousands of duplicates.
- Enabling two-way sync everywhere because it sounded safer.
- Syncing dozens of fields no report or workflow consumes.
- No alerting, so a broken sync surfaces in a board meeting.
- Renaming a HubSpot property without checking what integrates with it.
