What this workflow does
- HubSpot company changed (Webhook) receives HubSpot’s batch of change events.
- Filter events (loop prevention) (Code) keeps one item per company, ignores properties the sync writes itself and ignores fields that are not synced.
- HubSpot: get company (HTTP Request) reads the current company through the CRM v3 API, including the stored QuickBooks ID and hash.
- Map fields and detect changes (Code) maps HubSpot fields to QuickBooks fields, converts state names to two-letter codes and hashes the result. If the hash matches the stored one, nothing that QuickBooks cares about changed and the item stops here.
- Sync action (Switch) routes to update (ID on file), match (no ID yet) or exception (no name, unmappable state).
- QuickBooks: find candidates (HTTP Request) queries QuickBooks for customers with a similar display name.
- Build match prompt, Claude: score fuzzy match and Decide match ask Claude, at low effort with structured outputs, whether any candidate is the same business. Only a high-confidence answer links or creates automatically. Anything uncertain, plus
refusalandmax_tokensresponses, goes to review. - Match result (Switch) creates a new customer, links an existing one through the update path, or sends it for review.
- QuickBooks: update customer and QuickBooks: create customer write the fields. HubSpot: store QuickBooks ID and hash saves the mapping, which prevents duplicates next time.
- Slack: sync exceptions collects every failure and uncertain match, with candidate IDs.
Before you import
- n8n 1.x and a public webhook URL.
- A HubSpot private app token with company read and write scopes. The same private app can hold the webhook subscription in its Webhooks tab; apps built with HubSpot projects cannot.
- A QuickBooks Online OAuth2 credential in n8n (an Intuit developer app). Start in the sandbox.
- An Anthropic API key and a Slack app with
chat:write.
Setup
- Create the HubSpot company properties
qbo_customer_id,qbo_sync_hashandqbo_last_synced_at. - Subscribe to
company.propertyChangefor name, domain, phone, address, city, state and zip, pointed at the webhook URL. - Replace
REPLACE_WITH_QBO_REALM_ID. While testing, switch the host tosandbox-quickbooks.api.intuit.comand set the QuickBooks credential’s environment to Sandbox. - Replace
REPLACE_WITH_SLACK_CHANNEL_IDandREPLACE_WITH_HUBSPOT_PORTAL_ID. - Review
FIELD_MAP. HubSpot owns every mapped field, so a blank value in HubSpot clears it in QuickBooks. - Before going live, match your existing records once so most companies already have a stored ID.
Test it
Edit the phone on a HubSpot company with a stored ID and confirm the QuickBooks customer updates within a minute. Edit a property that is not synced and confirm nothing runs past the hash check. Create a company named slightly differently from an existing customer and confirm it either links or lands in Slack for review, never creating a duplicate.
Make it production-ready
- Add a nightly reconciliation workflow that compares hashes on both sides in report-only mode first.
- Decide how deletes and HubSpot merges should behave, and handle merges by updating the stored ID.
- QuickBooks and HubSpot both enforce API rate limits. Batch large backfills and keep the built-in retries.
- Log each write (company, customer ID, action, hash) to a table for audit.
claude-haiku-5-5is a cheaper option for match scoring at volume.
Read the full guide at How to sync data between business apps automatically, compare platforms in n8n vs Zapier, or talk to us about a two-way sync.