Attio and HubSpot do not connect to each other out of the box. If your team runs HubSpot for marketing and Attio for sales, every contact, company and deal lives in two systems with no built-in bridge between them. The integration is yours to build, and the decisions you make about field ownership and matching keys matter more than the tool you pick. Our Attio consulting practice helps teams design exactly this.
Attio has no native HubSpot integration. The four ways to connect them are a dedicated sync tool (Outfunnel, Polytomic or Stacksync), Zapier for simple hand-offs, n8n for custom logic with retries and logging, or a one-time migration when you are actually moving. Before choosing a tool, agree on field ownership: which system is the source of truth for each field, and which direction each field syncs.
Why teams run Attio and HubSpot side by side
The most common split is marketing in HubSpot, sales in Attio. HubSpot's marketing automation, email sequences and form infrastructure are mature and hard to replace. Attio's flexible data model and relationship intelligence make it a better fit for teams that sell into complex accounts. The result is a two-CRM setup where leads enter through HubSpot and move to Attio once they qualify.
Other patterns exist. Some teams keep HubSpot as the system of record and use Attio as a lightweight pipeline view for a small sales team. Others run Attio for new business and keep HubSpot for customer success and renewals. In every case, the same problem shows up: records that exist in both systems drift apart within weeks unless a sync keeps them aligned.
If you are evaluating whether to keep both or consolidate, our HubSpot vs Attio comparison covers that decision. If you have already decided to move everything to Attio, the migration guide is the right starting point. This post is for teams that keep both.
Decide field ownership before choosing a tool
The tool you pick to sync the two systems matters less than the ownership contract you agree on first. Every field in the sync needs exactly one owner: the system that is allowed to write to it. If both systems write to the same field, the last write wins, and you end up with silent overwrites that nobody notices until a rep calls a contact with the wrong title.
| Object / field | Owner | Sync direction | Conflict rule |
|---|---|---|---|
| Contact email | HubSpot | HubSpot to Attio | Never overwrite in Attio |
| Contact name | HubSpot | HubSpot to Attio | Attio can update if HubSpot is blank |
| Company domain | HubSpot | HubSpot to Attio | Never overwrite |
| Company name | Attio | Attio to HubSpot | HubSpot can update if Attio is blank |
| Lifecycle stage | HubSpot | HubSpot to Attio | Never overwrite in Attio |
| Lead status | HubSpot | HubSpot to Attio | Never overwrite |
| Deal stage | Attio | Attio to HubSpot | Never overwrite in HubSpot |
| Deal amount | Attio | Attio to HubSpot | Last write from Attio wins |
| Industry | Either (pick one) | One direction only | Owner always wins |
| Phone | HubSpot | HubSpot to Attio | Overwrite if blank |
| Job title | Attio | Attio to HubSpot | Overwrite if blank |
The "overwrite if blank" rule is the safest default for fields that both teams touch. It means the sync fills in gaps without replacing data a human entered. For fields that decay quickly (technographics, headcount), you may want "always overwrite" from whichever system runs enrichment.
This table is a starting template. Adapt it to your team's workflow. For a broader framework on who owns which field across your entire stack, see the data governance guide.
Matching keys: email for people, domain for companies
Every sync needs a matching key: the field that tells the integration whether a record already exists in the other system. Get the key wrong and you create duplicates. Get it right and you still need to handle the edge cases.
Contacts match on email. Both HubSpot and Attio treat email as the primary identifier for people. HubSpot's contacts API describes email as "the primary unique identifier to avoid duplicate contacts." Attio's record query endpoint filters people by email values. This works for most B2B contacts, but it breaks when a contact changes jobs and gets a new email, or when two people share a group inbox address.
Companies match on domain. HubSpot's companies API describes domain as "the primary unique identifier to avoid duplicate companies." Attio stores domains as an array attribute on company records. Domain matching fails for subsidiaries that share a parent domain (acme.com and uk.acme.com may be separate accounts in your CRM) and for companies that operate multiple domains.
Store the foreign ID. The most reliable way to keep records linked across both systems is to store each system's record ID on the other side. Create a custom property in HubSpot called attio_record_id and a custom attribute in Attio called hubspot_record_id. When the sync creates or matches a record, write the ID back. Every subsequent sync operation should look up by stored ID first, then fall back to email or domain only for records that have never been matched.
This pattern prevents duplicates even when someone changes their email address or a company switches domains. When matching keys break and duplicates pile up, the CRM audit guide shows how to find and merge them.
Your four integration options
Option 1: a dedicated sync tool
Three tools sync Attio and HubSpot as a core use case. Each takes a different approach.
Outfunnel syncs contacts and company fields between Attio and HubSpot. Outfunnel's integration page confirms that "there's no native integration available between Attio and HubSpot" and positions itself as the bridge. You choose the sync direction per field (one-way or two-way). List membership checks run every two hours. Field changes for already-synced contacts update in real time. Outfunnel covers contacts and companies but does not sync deals, custom objects or activities.
Polytomic syncs broader objects between the two systems. Polytomic's Attio-HubSpot page describes three write modes: create only, update only, or create and update. Both systems key companies on domain. Polytomic can add records to a named Attio list during sync, which is useful for audience segmentation.
Stacksync covers the widest object set. Stacksync's integration page lists people, companies, deals, custom objects on the Attio side, and contacts, companies, deals, line items, products, tickets, quotes, custom objects, associations and activity objects on the HubSpot side.
The trade-off across all three: they handle the plumbing but give you limited control over error handling, retry logic and transformation. If a sync fails on a single record, the tool decides what happens next.
Option 2: Zapier for simple hand-offs
Zapier connects Attio and HubSpot with triggers and actions. Attio's Zapier triggers are webhook-based, which means they fire instantly when a record changes, with no polling delay. The Attio Zapier integration includes a "Create or Update Record" action that matches on a unique attribute and updates if a match exists or creates if it does not.
Zapier works well for one-directional hand-offs: a new MQL in HubSpot creates a person in Attio, or a closed-won deal in Attio updates a company property in HubSpot. It is less suited for ongoing two-way field sync because each direction needs its own Zap, and there is no built-in loop detection. A change in system A triggers a Zap that writes to system B, which triggers a Zap back to system A.
Option 3: n8n for custom logic, retries and logging
n8n is where most complex Attio-HubSpot integrations land. n8n has a built-in HubSpot node with operations for contacts, companies, deals, tickets and engagements. Attio does not have an official built-in n8n node, so you connect to the Attio API through the HTTP Request node.
The advantage over sync tools and Zapier is control. You define the lookup logic, the matching key, the fallback, the error branch, the retry count, and the log. You can transform data mid-flow, apply conditional logic ("only sync contacts in lifecycle stage MQL or later"), and write structured error records to a monitoring sheet or Slack channel.
The cost is build time. How long depends on the number of objects you sync, whether any fields run two-way, and how many edge cases (email changes, subdomain matching, blank-field handling) your workflow has to cover. We build these integrations as part of our n8n automation practice. The n8n guide goes deeper on n8n for revenue operations.
Option 4: a one-time migration
If you are not actually running both systems long term but moving from HubSpot to Attio, the right tool is Attio's built-in migration from another CRM. This uses Import2 to transfer companies, people, deals, notes, tasks, attachments and custom objects. Shuttio (attio.com/apps/shuttio) is a separate migration app that supports re-runs without creating duplicates by matching people on email and companies on domain.
This is a one-time move, not an ongoing sync. If you want both systems running in parallel afterward, use one of the three options above. The HubSpot to Attio migration guide lays out the full cutover plan, and our HubSpot migration practice handles the execution. Both sit under the broader CRM practice.
Building the sync in n8n, step by step
This section walks through a contact sync from HubSpot to Attio. The same pattern applies in reverse or for companies and deals. The logic is: look up by stored ID, then by email or domain, update if found, create if not, write the ID back, log errors.
Step 1: Trigger the workflow. Use a Schedule Trigger node set to your sync interval (every 15 minutes or every hour depending on volume) or a HubSpot trigger node that fires on contact property changes.
Step 2: Fetch updated HubSpot contacts. Use the HubSpot node's Search Contacts operation to find contacts where lastmodifieddate is greater than the last sync timestamp. Request the properties you need: email, firstname, lastname, lifecyclestage, and your custom attio_record_id property.
Step 3: Look up the Attio record by stored ID. For each contact, check whether attio_record_id is populated. If it is, use an HTTP Request node to GET the Attio person record at https://api.attio.com/v2/objects/people/records/{id}. If the request returns 200, you have your match.
Step 4: Fall back to email lookup. If attio_record_id is blank or the GET returned 404, use an HTTP Request node to POST to the Attio records query endpoint filtered by email. If a match comes back, capture the Attio record ID from the response.
Step 5: Update or create. If a match was found (by ID or email), PATCH the existing Attio record with the synced fields. If no match was found, POST to create a new person record. In both cases, capture the Attio record ID from the response. A simpler alternative for the email fallback: Attio's assert endpoint (PUT /v2/objects/people/records with matching_attribute=email_addresses) creates or updates in a single call, so you can skip the separate query-then-create branch.
Step 6: Write the Attio record ID back to HubSpot. Use the HubSpot node's Update Contact operation to set the attio_record_id property. This ensures the next sync run looks up by stored ID, not by email.
Step 7: Log errors and handle retries. Add an Error Trigger node. Route failed items to a Google Sheet, Slack channel or database row that records the HubSpot contact ID, the error message and a timestamp. Set the HTTP Request nodes to retry on 429 (rate limit) responses with exponential backoff. Both APIs enforce rate limits: HubSpot's API usage guidelines document per-app and daily caps, and Attio's API returns 429 when you exceed the limit.
Keeping deals, lifecycle stages and consent in line
Contacts and companies are the foundation, but three objects need their own sync rules.
Deals. If Attio owns the pipeline and HubSpot needs deal data for reporting, sync the deal amount, close date and stage from Attio to HubSpot. Map Attio stages to HubSpot deal stages explicitly. Do not sync deal stage bidirectionally because two systems moving a deal through a pipeline at different speeds creates contradictions that confuse forecasting.
Lifecycle stages. HubSpot's default Lifecycle stage property only moves forward when set by the API, imports, forms or workflows. A sync that tries to write an earlier stage (say, pushing "Lead" onto a contact already at "Customer") will silently fail to apply. The HubSpot Knowledge Base confirms you must clear the value before these tools can set an earlier stage. Never build a sync that clears and resets the lifecycle stage to get around this, because that demotes the record and muddies the stage history your funnel reports rely on. Only allow forward moves in the sync, and treat HubSpot as the owner of this field.
Consent and unsubscribe status. This is the one field where "last write wins" can cause legal problems. If a contact unsubscribes in HubSpot, that opt-out must propagate to Attio immediately. It must never be overwritten by a sync that runs on a schedule and pushes an older "subscribed" value. Sync consent status from the system that handles email (usually HubSpot) to the other system, one direction only, and never allow the receiving system to write back. The GDPR opt-out sync guide details the broader pattern across multiple systems.
Test before you switch it on
A sync that touches both CRMs at once can create hundreds of duplicates in minutes if the matching logic is wrong. Test methodically.
Start with 10 to 20 records. Pick contacts and companies that cover each scenario: records that exist in both systems, records that exist only in HubSpot, and records that exist only in Attio. Include edge cases: a contact with a changed email, a company with a subdomain, a record with blank fields in the ownership table.
Run the sync once on the sample. Check each record in both systems afterward. Verify that existing records were updated (not duplicated), new records were created in the right system, and fields marked "never overwrite" were left untouched.
Record reconciliation counts. Before going live, count contacts and companies in each system. After the first full sync, count again. The numbers should increase by net-new records only. If total records in either system jumped by more than expected, the matching logic missed records and created duplicates.
Monitor the first week. Set up a simple check: count records in each system daily for a week. A steady increase that matches your new-record volume means the sync is working. A spike means the matching key failed on a batch. If duplicates appear, the CRM audit guide walks through finding and merging them. For long-term drift prevention, see the data governance guide.
FAQ
Does Attio integrate with HubSpot natively?
No. Attio's app directory has no native HubSpot sync integration. The directory does carry migration apps that pull data from HubSpot into Attio (Shuttio, Import2), but those are one-time movers, not ongoing connectors. Outfunnel confirms there is "no native integration available between Attio and HubSpot." To sync the two systems you need a third-party tool, Zapier, n8n, or a custom API integration.
How do I sync contacts and companies between Attio and HubSpot?
Choose a tool that matches your complexity. For simple contact and company field sync, Outfunnel handles per-field direction and real-time updates. For broader object coverage including deals and custom objects, Stacksync or Polytomic cover more ground. For full control over matching logic, transformation and error handling, build the sync in n8n using the HubSpot node and Attio's API.
Can I use HubSpot for marketing and Attio as my sales CRM?
Yes. This is the most common two-CRM pattern. HubSpot handles forms, email automation, landing pages and lifecycle management. Attio handles the pipeline, relationship tracking and deal progression. The key is agreeing which system owns which fields and syncing contacts from HubSpot to Attio at a defined handoff point, usually when a lead reaches MQL or a specific lifecycle stage.
How do I stop duplicates when syncing Attio and HubSpot?
Match contacts on email and companies on domain as the primary key. Store each system's record ID as a custom property on the other side (attio_record_id in HubSpot, hubspot_record_id in Attio) and look up by stored ID first on every sync run. Create a new record only when both the ID lookup and the email or domain lookup return no match. The Clay to HubSpot write-back guide breaks down the same lookup-before-create pattern.
Should I use a sync tool, Zapier or n8n to connect Attio and HubSpot?
It depends on what you are syncing and how much control you need. A dedicated sync tool (Outfunnel, Polytomic or Stacksync) is fastest to set up and handles the common case of bidirectional contact and company sync. Zapier works for one-directional hand-offs where a trigger in one system creates or updates a record in the other. n8n is the right choice when you need conditional logic, structured error logging, retry handling or transformations that the other tools cannot express.
What is the best matching key for preventing duplicates across both CRMs?
Email for contacts and domain for companies, with a stored foreign ID as the primary lookup once the initial match is made. HubSpot's contacts API uses email as the unique identifier, and HubSpot's companies API uses domain. Attio matches on the same fields. Store the cross-system record ID and look up by that first to handle email changes, domain switches and subsidiary merges.
Running Attio and HubSpot together and need help designing the sync? We build these integrations.
Talk to us