---
title: The CTO's Guide to Salesforce to Attio Migration (2026)
slug: the-ctos-guide-to-salesforce-to-attio-migration-2026
date: 2026-06-03
author: Raaj
categories: [Salesforce, Migration Guide, Salesforce Service Cloud, Attio]
excerpt: "A CTO-level guide to migrating from Salesforce to Attio. Covers data model mapping, migration methods, API rate limits, edge cases, and validation."
tldr: "Salesforce to Attio migration requires schema translation, not lift-and-shift. Use Bulk API 2.0 for extraction, Attio's assert endpoint for upsert, and strict dependency ordering to preserve relationships."
canonical: https://clonepartner.com/blog/the-ctos-guide-to-salesforce-to-attio-migration-2026/
---

# The CTO's Guide to Salesforce to Attio Migration (2026)


Migrating from Salesforce to Attio is a schema translation problem, not a lift-and-shift. Salesforce stores business meaning in record types, validation rules, roll-up summaries, Apex triggers, and deeply nested object hierarchies. Attio stores meaning in objects, attributes, relationship fields, lists, and views. Every object, field, and relationship must be deliberately remapped — and several Salesforce-specific constructs (formula fields, roll-up summaries, Person Accounts) have no direct Attio equivalent.

If your engineering team attempts to export Salesforce data via CSV and push it into Attio without restructuring the schema, you will flatten your data, orphan historical activities, and sever the relationship chains (Account → Contact → Opportunity) that give your CRM its value. ([attio.com](https://attio.com/help/reference/attio-101/attios-data-model/define-your-data-model-objects-lists-and-views?utm_source=openai))

This guide covers the architectural differences, every viable migration method and its trade-offs, concrete object and field mappings, the API constraints on both sides, and the edge cases that silently destroy data integrity. For a deeper look at Attio's data model and workspace configuration, see [The Ultimate Guide to Attio CRM (2026)](https://clonepartner.com/blog/blog/ultimate-guide-attio-crm-2025/).

## Why Teams Leave Salesforce for Attio

The migration drivers we see most often:

- **Over-engineered orgs.** Years of Apex triggers, validation rules, Process Builder flows, and custom objects create a Salesforce org that's expensive to maintain and terrifying to change. Attio's schema is code-free and reconfigurable in minutes.
- **Cost.** A 10-person team on Salesforce Enterprise pays roughly $1,750/month. The same team on Attio Pro pays $690/month — and gets custom objects, relationship attributes, and AI-native features without admin overhead.
- **Relationship intelligence.** Attio automatically ingests email and calendar data from connected inboxes and enriches People and Company records without manual entry. Salesforce requires third-party tools or custom Apex to replicate this.
- **Data model flexibility.** Attio lets you create custom objects, define many-to-many relationships natively, and build lists that separate workflow context from record-level truth — all without writing code.

## The Paradigm Shift: Salesforce's Rigid Schema vs. Attio's Graph Model

Understanding the architectural gap is the prerequisite for every migration decision.

**Salesforce** organizes data around standard objects (Accounts, Contacts, Leads, Opportunities, Cases) connected by lookup and master-detail relationships. Customization happens through metadata: Record Types, Page Layouts, formula fields, roll-up summaries, and Apex code. Every customization adds schema weight. Many-to-many relationships require custom junction objects. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.customize_recordtype.htm&language=en_US&utm_source=openai))

**Attio** models data as **objects** and **records** with **relationship attributes** that link any object to any other object. Lists provide workflow context (pipeline stages, status tracking) without modifying the underlying record. There are no junction objects, no Record Types, and no formula fields. ([attio.com](https://attio.com/help/reference/attio-101/attios-data-model/define-your-data-model-objects-lists-and-views?utm_source=openai))

The practical impact: a Salesforce Account with three Record Types (Customer, Partner, Prospect) and a dozen Page Layouts becomes a single Attio Company with different list memberships and status attributes. The data simplifies, but the mapping decisions are non-trivial.

> [!WARNING]
> Attio has no equivalent for Salesforce formula fields, roll-up summaries, or Apex triggers. Any computed field values must be flattened to static values during migration or recreated using Attio workflows and AI attributes.

## Evaluating Salesforce to Attio Migration Approaches

There are five viable approaches. Pick based on data shape, history depth, and engineering bandwidth — not on which option looks fastest in a demo.

### 1. Native CSV Export/Import

**How it works:** Export Salesforce data using Data Loader or the Data Export Service as CSV files. Clean and transform in a spreadsheet or script. Import into Attio using the built-in CSV importer.

**When to use it:** Small datasets (under 5,000 records), flat structures with minimal cross-object relationships.

**Limitations:** Attio's CSV importer is capped at 100,000 rows, 100 columns, and 100 MB per file. It does not support importing notes. Deals require three mandatory fields — deal name, deal owner, and deal stage — and any missing value blocks the entire row. Relationships must be expressed through unique identifiers (email for People, domain for Companies), and the importer handles one object type per import. Salesforce's Data Export Service is not supported in sandboxes. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.admin_exportdata.htm&language=en_US&utm_source=openai))

**Complexity:** Low | **Scalability:** Poor for enterprise data

For a deeper analysis of CSV-based migration trade-offs, see [Using CSVs for SaaS Data Migrations: Pros and Cons](https://clonepartner.com/blog/blog/csv-saas-data-migration/).

### 2. Import2 (Attio's Built-in Migration Partner)

**How it works:** Attio offers a built-in migration path via Import2, accessible from Settings → Migrate on Plus, Pro, and Enterprise plans. Import2 connects directly to your Salesforce org and maps standard objects.

**Supported objects:** Companies, Contacts (Leads imported as Contacts), Deals, Notes, Tasks, Attachments, and Calls (imported as Notes). Emails and Meetings are **not supported**. Cases are **not supported**. ([attio.com](https://attio.com/help/reference/imports-exports/migrate-data-from-another-crm?source=footer_pipedrive))

**Key constraints:** Import2 creates new records but will not update existing records. If your team's email sync has already populated Attio with People and Company records, Import2 will create duplicates — it cannot upsert. Objects can only map to Attio objects, not to lists.

**Complexity:** Low | **Scalability:** Adequate for small-to-mid orgs with standard objects

For a detailed comparison of Import2's limitations against managed services, see [ClonePartner vs. Import2](https://clonepartner.com/blog/blog/clonepartner-vs-import2/).

### 3. API-Based Migration (Salesforce Bulk API 2.0 → Attio REST API)

**How it works:** Extract data from Salesforce using Bulk API 2.0 for high-volume queries and REST API for metadata. Transform in a middleware layer (Python, Node.js, or an ETL tool). Load into Attio via its REST API using the `POST /v2/objects/{object}/records` endpoint or the `assert` endpoint for upsert behavior.

**When to use it:** Any migration with more than 10,000 records, custom objects, multi-level relationships, or data that needs transformation during transit. Salesforce documents REST Query at up to 2,000 records per response with query locators for pagination, and Bulk API 2.0 for large asynchronous result sets. ([developer.salesforce.com](https://developer.salesforce.com/blogs/2024/04/accessing-object-data-with-salesforce-platform-apis?utm_source=openai))

**Scalability:** This is the only approach that scales to enterprise Salesforce orgs.

**Complexity:** High — requires engineering resources, error handling, and rate limit management on both sides.

### 4. Custom ETL Pipeline

**How it works:** Build a dedicated Extract-Transform-Load pipeline using tools like Airbyte, Fivetran, or custom scripts that stage data in a warehouse (PostgreSQL, BigQuery) before loading to Attio. Attio exposes attribute creation and generic record upsert endpoints, so schema-first loading is practical. ([docs.attio.com](https://docs.attio.com/rest-api/endpoint-reference/attributes/create-an-attribute?utm_source=openai))

**When to use it:** Large, complex Salesforce orgs where you need full control over transformation logic, deduplication, audit trails, deterministic ID maps, and repeatable dry runs.

**Complexity:** High | **Scalability:** Enterprise-grade

### 5. Middleware (Zapier, Make)

**How it works:** Configure triggers and actions to move records between Salesforce and Attio through a visual workflow builder. Zapier and Make both have verified modules for Salesforce and Attio. ([zapier.com](https://zapier.com/apps/attio/integrations/salesforce?utm_source=openai))

**When to use it:** Ongoing sync of small record volumes during a phased migration or parallel run. Not suitable for bulk historical migration.

**Limitations:** Record-by-record execution. Neither platform handles complex relationship chains, dependency ordering, or bulk reconciliation.

**Complexity:** Medium | **Scalability:** Poor for historical data

### Which Approach Fits Your Scenario

- **Small business, <5K records, minimal custom objects:** CSV import or Import2.
- **Mid-market, 5K–100K records, standard + some custom objects:** Import2 for core objects, API scripts for custom objects and notes.
- **Enterprise, >100K records, deep Salesforce customization:** API-based migration or managed service. CSV and Import2 cannot handle the dependency chains, relationship resolution, or volume.
- **Ongoing sync required:** API-based with webhook listeners or a managed continuous sync service.
- **Low engineering bandwidth:** Managed migration service. The hidden cost of a senior engineer spending three weeks debugging rate limits and relationship ordering almost always exceeds the service fee.

> [!WARNING]
> If Activities and Cases matter, do not scope the migration around CSV alone. Attio's CSV importer does not import notes, and the Import2 Salesforce path does not migrate Cases.

## Pre-Migration Planning

Before writing extraction scripts, baseline your Salesforce org. Complex orgs hide business logic in triggers and flows that dictate data shape.

> [!NOTE]
> Take a full Salesforce backup first. The native Data Export Service produces org-wide CSV backups and can include attachments and Files. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.admin_exportdata.htm&language=en_US&utm_source=openai))

1. **Data inventory:** Quantify Accounts, Contacts, Leads, Opportunities, Cases, Tasks, Events, Campaigns, Attachments/Files, and every custom object that still matters.
2. **Complexity assessment:** Identify Apex triggers, validation rules, record types, and Process Builder/Flow automations. These often populate fields automatically in Salesforce — in Attio, you must migrate the computed values directly. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.customize_recordtype.htm&language=en_US&utm_source=openai))
3. **Scope definition:** Decide what *not* to migrate. Orphaned records, stale leads from five years ago, deprecated custom objects, and unused picklist values should be archived, not moved.
4. **Cutover model:** Determine whether you need a big-bang migration, phased approach, or incremental sync.
5. **Rollback assets:** Plan source backups, rerunnable loaders, a source-to-target ID ledger, and a target delete strategy.

For a complete planning framework, review [Best Practices for CRM Data Migration in 2026: The Engineer's Guide](https://clonepartner.com/blog/blog/best-practices-for-crm-data-migration-in-2026-the-engineers-guide/).

## Data Model Mapping: Salesforce Objects to Attio

Attio gives you objects for core entities and lists for workflow context. That distinction is the center of a good migration design. Use objects for records that should exist once. Use lists when the data is specific to a workflow, campaign, or stage. ([attio.com](https://attio.com/help/reference/attio-101/attios-data-model/define-your-data-model-objects-lists-and-views?utm_source=openai))

### Core Object Mapping

- **Accounts → Companies.** Map `Account.Name` to Company name, `Account.Website` to Company domain (Attio uses domain as the unique identifier and enrichment key). Attio natively enriches Companies based on domain names. Account Record Types have no Attio equivalent — use list membership or a custom select attribute to preserve the distinction. Attio documents `domains` as unique and says Companies cannot have new custom unique attributes. ([docs.attio.com](https://docs.attio.com/docs/standard-objects/standard-objects-companies))
- **Contacts → People.** Map `Contact.Email` to People email (unique identifier). `Contact.AccountId` resolves to a relationship attribute linking People → Companies. Attio documents `email_addresses` as the unique attribute for People upserts, so contacts without reliable email need a separate merge policy. ([docs.attio.com](https://docs.attio.com/rest-api/endpoint-reference/people/upsert-a-person-record?utm_source=openai))
- **Leads → People.** Attio has no Lead object. Import Leads as People with a status attribute or list membership that preserves lead stage context. Deduplicate against existing Contacts by email before migration to prevent duplicates.
- **Opportunities → Deals.** Attio's Deals object requires `deal_name`, `deal_owner`, and `deal_stage` for every record. Map `Opportunity.StageName` to Attio deal stages — define these in your pipeline list before migration. `Opportunity.Amount` maps to a currency attribute. Deals have no unique attribute by default, so add your own if you want upsert behavior via the `assert` endpoint. ([docs.attio.com](https://docs.attio.com/rest-api/attribute-types/attribute-types-status?utm_source=openai))
- **Activities (Tasks, Events) → Notes / Tasks.** Attio notes link to a single parent record. If a Salesforce Task relates to both a Contact and an Opportunity, you must choose one parent or create two copies. Closed history usually fits Notes better than Tasks — use Tasks only for open work. ([docs.attio.com](https://docs.attio.com/rest-api/endpoint-reference/notes/create-a-note?utm_source=openai))
- **Cases → Custom Object or Notes.** Attio has no Cases equivalent. Simple case history can be flattened to Notes. Complex case management with status tracking requires a custom object with relationship attributes to People and Companies. Import2 will not migrate Salesforce Cases.
- **Campaign Members → Lists or Tags.** Map campaign membership to Attio list entries or tag attributes.
- **Custom Objects → Custom Objects in Attio.** Evaluate per-object feasibility. Attio custom object limits vary by plan: 3 (Free), 5 (Plus), 12 (Pro), unlimited (Enterprise).

### Field-Level Mapping

**Accounts → Companies:**
`Account.Name` → Company name (text). `Account.Website` → Domains (domain) — critical for enrichment; preserve full URL in a custom text field if needed. `Account.Phone` → Phone numbers. `Account.BillingAddress` → Primary location. `Account.Industry` → Custom select attribute. `Account.AnnualRevenue` → Custom number/currency attribute. `Account.OwnerId` → Workspace member via owner attribute. ([docs.attio.com](https://docs.attio.com/docs/standard-objects/standard-objects-companies))

**Contacts → People:**
`Contact.FirstName` / `Contact.LastName` → Name (personal-name). `Contact.Email` → Email addresses (unique — Attio stores emails as an array). `Contact.Phone` → Phone numbers. `Contact.Title` → Job title. `Contact.MailingAddress` → Primary location. `Contact.AccountId` → Company relationship attribute (resolved by domain or record ID). ([docs.attio.com](https://docs.attio.com/docs/standard-objects/standard-objects-people))

**Opportunities → Deals:**
`Opportunity.Name` → Deal name. `Opportunity.StageName` → Deal stage (a status attribute, not free text — unknown statuses will error out). `Opportunity.Amount` → Deal value (ensure currency formatting matches). `Opportunity.CloseDate` → Custom date attribute. `Opportunity.AccountId` → Associated company (relationship attribute). ([docs.attio.com](https://docs.attio.com/docs/standard-objects/standard-objects-deals?utm_source=openai))

**Activities → Notes:**
`Task/Event.Subject` → Note title. `Task/Event.Description` → Note content. `Task/Event.CreatedDate` → Note `created_at`. `Task/Event.WhoId`/`WhatId` → Parent record reference. ([docs.attio.com](https://docs.attio.com/rest-api/endpoint-reference/notes/create-a-note?utm_source=openai))

> [!NOTE]
> Salesforce picklist values must be normalized to match exact Attio select option labels before import. Attio will not auto-create unknown select or status values — it rejects them. Multi-select values use commas as delimiters, meaning picklist values containing commas will break the CSV importer. Create all target select options and deal statuses before the first load. ([docs.attio.com](https://docs.attio.com/docs/attribute-types/attribute-types-select))

### Handling Salesforce-Specific Constructs

- **Record Types:** Convert to custom single-select attributes in Attio, or use list membership to maintain categorization. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.customize_recordtype.htm&language=en_US&utm_source=openai))
- **Formula Fields & Roll-up Summaries:** Attio does not compute historical roll-ups natively. Query the evaluated output of these fields in Salesforce and map them as static values in Attio. Document what's lost. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.fields_about_roll_up_summary_fields.htm&language=en_US&type=5&utm_source=openai))
- **Lookup/Master-Detail Relationships:** Become Attio record reference attributes. ([docs.attio.com](https://docs.attio.com/docs/attribute-types/attribute-types-record-reference?utm_source=openai))
- **Person Accounts:** These combine Account and Contact fields into a single hybrid record. Split deliberately into People plus Companies, or design a B2C-specific pattern. Do not let the mapper guess. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.account_person.htm&language=en_US&type=5&utm_source=openai))

## Migration Architecture: Navigating API Limits

Extracting data from Salesforce and loading it into Attio is a battle against governor limits on both sides.

### Salesforce Extraction Constraints

Salesforce Enterprise Edition enforces a **100,000 daily REST/SOAP API request limit** plus 1,000 per user license, calculated on a 24-hour rolling window. This allocation is shared across your entire org — naive REST API scripts that query records one-by-one will exhaust the limit and shut down integrations company-wide. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=xcloud.setup_system_overview_api.htm&language=en_US&type=5&utm_source=openai))

For large historical extractions, use **Salesforce Bulk API 2.0**. Query jobs don't consume batch allocations, and data operations support up to **15,000 batches per 24 hours** with **10,000 records per batch**. Use SOQL for precise field selection — never use `FIELDS(ALL)` in production extraction.

Authentication requires an OAuth 2.0 Connected App. Salesforce restricted creation of new connected apps in Spring '26 and recommends external client apps for new setups. ([developer.salesforce.com](https://developer.salesforce.com/docs/platform/mobile-sdk/guide/connected-apps.html?utm_source=openai))

### Attio Loading Constraints

Attio's API enforces **100 read requests/second** and **25 write requests/second** using a 10-second sliding window. There is no Bulk API equivalent — every record is a single API call. At 25 writes/sec, importing 50,000 records takes a minimum of ~33 minutes of pure write time. HTTP 429 responses include a `Retry-After` header. ([docs.attio.com](https://docs.attio.com/rest-api/guides/rate-limiting?utm_source=openai))

Authentication uses API access tokens generated in Workspace Settings → Developers, scoped per integration. The `assert` endpoint with `matching_attribute` enables upsert behavior — critical for avoiding duplicates when re-running migration batches. Attio also supports OAuth 2.0 for integration authentication.

Some Attio endpoints use limit/offset pagination, others use cursor-based pagination with `next_cursor`. Build your scripts to handle both.

> [!CAUTION]
> Disable Attio email sync for all workspace members **before** starting migration. If connected, Attio auto-creates People and Company records from incoming emails, and your migration script will create duplicates. Re-enable sync only after migration validation is complete.

## Step-by-Step Migration Process

**Step 1 — Pre-Migration Audit.** Inventory Salesforce objects. Assess org complexity: count custom objects, active Apex triggers, validation rules, and Flow automations that alter data shape. Identify and exclude stale data.

**Step 2 — Design Attio Schema.** Define all target objects, attributes, pipeline lists, status stages, and select options in Attio **before importing any data**. Validate that every Salesforce field has a mapped Attio attribute with the correct type. ([attio.com](https://attio.com/help/reference/imports-exports/migrate-data-from-another-crm?source=footer_pipedrive))

**Step 3 — Extract from Salesforce.** Use Bulk API 2.0 for large objects and REST API for metadata. Extract in dependency order: Users → Accounts → Contacts → Leads → Opportunities → Activities → Custom Objects. Maintain a Salesforce ID → Attio Record ID mapping table.

**Step 4 — Transform.** Clean duplicates. Normalize picklist values to match Attio select labels exactly. Flatten formula fields to static values. Resolve relationship IDs. Convert Record Types to Attio select attributes or list memberships.

**Step 5 — Load into Attio.** Import Companies first (matched by domain), then People (matched by email), then Deals (linked to Companies). Use the `assert` endpoint for upsert behavior. Load Notes and Tasks via API — not CSV. Rebuild cross-object references using the ID mapping table.

**Step 6 — Validate.** Compare record counts per object. Spot-check 20+ records per object across both systems. Verify relationship integrity. Run UAT with actual sales users. Keep Salesforce read-only for 2–4 weeks as a rollback safety net.

### Salesforce Extraction Example (Bulk API 2.0)

```text
POST /services/data/v62.0/jobs/query
Body:
  operation: query
  query: SELECT Id, Name, Website, OwnerId FROM Account

GET /services/data/v62.0/jobs/query/{job_id}
GET /services/data/v62.0/jobs/query/{job_id}/results
```

### Attio Load Example (Person Upsert + Note Create)

```text
PUT /v2/objects/people/records?matching_attribute=email_addresses
Body:
  data.values.email_addresses: [alice@example.com]
  data.values.name.first_name: Alice
  data.values.name.last_name: Nguyen
  data.values.company.target_object: companies
  data.values.company.target_record_id: <attio_company_id>

POST /v2/notes
Body:
  data.parent_object: people
  data.parent_record_id: <attio_person_id>
  data.title: Discovery call
  data.content: Flattened Salesforce task/event history
  data.created_at: 2025-11-18T14:30:00Z
```

### Python Migration Script Outline

```python
import requests
import time

SF_BULK_URL = "https://yourorg.salesforce.com/services/data/v62.0/jobs/query"
ATTIO_BASE = "https://api.attio.com/v2"
ATTIO_TOKEN = "Bearer <your-attio-token>"
ID_MAP = {}  # sf_id -> attio_record_id

def extract_salesforce(query, sf_token):
    """Submit Bulk API 2.0 query job and poll for results."""
    headers = {"Authorization": f"Bearer {sf_token}", "Content-Type": "application/json"}
    job = requests.post(SF_BULK_URL, json={"operation": "query", "query": query}, headers=headers)
    job_id = job.json()["id"]
    while True:
        status = requests.get(f"{SF_BULK_URL}/{job_id}", headers=headers).json()
        if status["state"] == "JobComplete":
            return requests.get(f"{SF_BULK_URL}/{job_id}/results", headers=headers).text
        time.sleep(5)

def load_to_attio(object_slug, records, matching_attr=None):
    """Load records to Attio with rate limit handling."""
    endpoint = f"{ATTIO_BASE}/objects/{object_slug}/records"
    if matching_attr:
        endpoint = f"{endpoint}?matching_attribute={matching_attr}"
    for record in records:
        while True:
            resp = requests.put(
                endpoint,
                json={"data": {"values": record}},
                headers={"Authorization": ATTIO_TOKEN, "Content-Type": "application/json"}
            )
            if resp.status_code == 429:
                wait = int(resp.headers.get("Retry-After", 2))
                time.sleep(wait)
                continue
            resp.raise_for_status()
            ID_MAP[record.get("sf_id")] = resp.json()["data"]["id"]["record_id"]
            break
```

For error handling, log object name, source ID, batch ID, payload hash, target ID, HTTP status, and retry count. Treat Attio 429s as retryable using the `Retry-After` header. Treat mapping and validation failures as dead-letter records, not silent skips.

## Edge Cases That Break Migrations

Migrations fail in the edge cases. Plan for all of these:

- **Person Accounts.** Salesforce Person Accounts merge Account and Contact fields into a hybrid object. Attio has no equivalent. Split them: create a People record for the individual and optionally a Company record if business context exists. Map Person Account fields across both objects. Define explicit B2B vs. B2C rules — do not let the mapper guess. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.account_person.htm&language=en_US&type=5&utm_source=openai))
- **Duplicate records.** Salesforce orgs accumulate years of duplicate Leads and Contacts. Deduplicate *before* migration. Attio uses email (People) and domain (Companies) as unique identifiers — duplicates in these fields will silently merge or fail. Shared inboxes and alias emails need explicit merge rules. ([docs.attio.com](https://docs.attio.com/rest-api/endpoint-reference/people/upsert-a-person-record?utm_source=openai))
- **Multi-level relationships.** A Task in Salesforce might be linked to an Opportunity, which is linked to a Contact, which is linked to an Account. Your script must resolve all three foreign keys and import in strict dependency order. The Attio API will reject a People record with a Company relationship reference if that Company doesn't yet exist.
- **Company matching surprises.** Attio can auto-generate or match a Company from a Person's email domain. This is helpful for greenfield CRM use, but risky during migration when subsidiaries, agencies, or personal email domains are in the data. Control this behavior during import. ([docs.attio.com](https://docs.attio.com/docs/standard-objects/standard-objects-companies))
- **Formula fields and roll-up summaries.** These are computed server-side in Salesforce and have no Attio equivalent. Export the computed values as static data during extraction. Document what's lost. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.fields_about_roll_up_summary_fields.htm&language=en_US&type=5&utm_source=openai))
- **File attachments.** Attio's file upload endpoint is in beta with a 50 MB max file size. Very large binaries or complex version history need separate planning — consider exporting files to cloud storage (Google Drive, S3) and linking via URL attributes or Notes. ([docs.attio.com](https://docs.attio.com/rest-api/endpoint-reference/files/upload-a-file?utm_source=openai))
- **Sandbox vs. Production.** Always run test migrations against a Salesforce sandbox first. Salesforce's Data Export Service is not supported in sandboxes — use Data Loader or APIs for test runs. Sandbox API limits can differ from production. ([help.salesforce.com](https://help.salesforce.com/s/articleView?id=sf.admin_exportdata.htm&language=en_US&utm_source=openai))

## Limitations You Must Accept

Attio is not Salesforce with a better UI. It's a different paradigm, and some capabilities don't translate:

- **No Apex-equivalent.** Complex server-side logic must be rebuilt as Attio workflows, external automations, or accepted as out of scope. Attio gives you workflows, a REST API, and an App SDK with server functions — but it is not a drop-in replacement for Salesforce Platform behavior. ([attio.com](https://attio.com/help/reference/automations/workflows/getting-started-with-workflows?utm_source=openai))
- **No native CPQ or approval chains.** If your Salesforce org relies on CPQ or multi-step approvals, Attio won't replace that functionality.
- **Reporting differs.** Attio's reporting is list- and view-based. Salesforce's report builder, dashboards, and cross-object reports are more mature. Plan to recreate critical reports manually.
- **Field history tracking doesn't migrate.** Salesforce audit trail and field history data has no Attio equivalent. If compliance requires historical audit logs, export them to a data warehouse rather than trying to force them into Attio.
- **Record limits matter.** Attio caps records per plan: 50K (Free), 250K (Plus), 1M (Pro). Verify your Salesforce record volume fits the target plan before starting.

## Validation, Testing, and Rollback

Never execute a production migration without a sandbox test first. Validation needs to be mechanical, not manual spot checks of random records.

- **Record count comparison:** Per object, per Salesforce Record Type. Every number must match.
- **Field-level validation:** Spot-check critical fields — currency values, dates, picklist integrity. Test high-value fields, not random trivia.
- **Relationship integrity:** Verify all People link to correct Companies. Confirm Deals associate to the right Companies and pipeline stages.
- **Edge case sampling:** Check merged leads, Person Accounts, missing domains, archived owners.
- **UAT:** Have actual sales, RevOps, and support users validate their accounts, open deals, and recent activity in Attio.
- **Rollback plan:** Keep Salesforce org read-only for 2–4 weeks post-migration. If issues surface, the original data is untouched. Maintain source backups, rerunnable loaders, and a cutover ledger.

## Post-Migration: Rebuilding the Operating Model

After loading data, rebuild the operating model in Attio. The data model and workflow builder give you the primitives, but they do not recreate old Salesforce behavior by themselves.

- **Rebuild automations.** Salesforce Flows and Process Builder rules don't transfer. Recreate critical automations as Attio workflows.
- **Recreate reports.** Start with the 5–10 reports your team actually uses, not every Salesforce dashboard.
- **Re-enable email sync.** Configure sync settings to match against existing records only — don't let Attio auto-create new People from every email.
- **Train users.** Salesforce power users need to unlearn Record Types, Page Layouts, and the Lead-to-Contact conversion model. Attio's paradigm is different; invest in training on the object/list split.
- **Monitor for at least one full sales cycle.** Watch for data inconsistencies, missing relationships, and user-reported gaps.
- **Decommission Salesforce.** After parallel run validation, archive the Salesforce org. Don't delete it — keep it read-only for compliance and reference.

## Best Practices Checklist

- **Back up everything.** Full Salesforce Data Export before every dry run and before go-live.
- **Test in sandbox.** Run at least two full-fidelity test migrations before production.
- **Design Attio schema first.** Define all objects, attributes, lists, select options, and stages before importing a single record.
- **Disable email sync.** Prevent Attio from auto-creating records during migration.
- **Import in dependency order.** Companies → People → Deals → Notes → Tasks → List entries.
- **Use the assert endpoint.** Upsert behavior prevents duplicates when re-running migration batches.
- **Validate after each object.** Don't wait until the end to check record counts.
- **Document every mapping decision.** Future you will thank present you.
- **Automate repetitive transforms and ID mapping.** Manual work at scale is where data loss hides.

## When to Use a Managed Migration Service

DIY migrations fail when the org is complex enough that the engineering cost exceeds the service fee. Common failure modes: broken relationship chains, duplicate records from email sync timing, governor limit exhaustion mid-extraction, and formula field data loss.

If your Salesforce org has more than 50K records, custom objects, Person Accounts, or multi-level relationship chains (Account → Contact → Opportunity → Activity), the migration is a full engineering project — not a weekend task. Building a custom ETL pipeline for a one-off migration is an expensive distraction for product engineering teams.

ClonePartner handles Salesforce-to-Attio migrations with complete relationship preservation, Salesforce governor limit management, and Attio rate limit handling built into our pipeline. We migrate Companies, People, Deals, Notes, Tasks, and custom objects with full cross-object reference resolution and record-count parity validation. Zero downtime — your team keeps working in Salesforce until cutover.

If your org is simple, build it yourself. If your org has ten years of history and custom behavior, buying certainty is usually cheaper than learning in production.

> Migrating from Salesforce to Attio and need it done right? Our engineers will map your org, handle the relationship chains, and deliver validated data — typically in days, not weeks. Book a free 30-minute architecture review.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can I migrate Salesforce data to Attio using CSV?

Yes, but with major limitations. Attio's CSV importer is capped at 100,000 rows, 100 columns, and 100 MB per file, does not support importing notes, and handles one object type per import. It works for small, flat datasets under 5,000 records but breaks down for enterprise orgs with custom objects, multi-level relationships, or activity history.

### What Salesforce objects does Import2 support for Attio migration?

Import2 supports importing Companies, Contacts (Leads imported as Contacts), Deals, Notes, Tasks, Attachments, and Calls (as Notes) into Attio. It does not support Cases, Emails, or Meetings. Import2 also cannot update existing records or import into Attio lists — it only creates new records.

### What are Attio's API rate limits for migration?

Attio allows 100 read requests per second and 25 write requests per second, measured on a 10-second sliding window. There is no Bulk API — every record requires a single API call. At 25 writes/sec, importing 50,000 records takes a minimum of ~33 minutes. Exceeding limits returns HTTP 429 with a Retry-After header.

### How do Salesforce Leads map to Attio?

Attio has no Lead object. Salesforce Leads should be imported as People records, with lead stage context preserved via a status attribute or list membership. Deduplicate Leads against existing Contacts by email before migration to prevent duplicates.

### What Salesforce features have no Attio equivalent?

Attio has no equivalent for formula fields, roll-up summaries, Record Types, Page Layouts, Apex triggers, CPQ, complex approval chains, or field history tracking. Formula field values must be flattened to static data during extraction. Record Types can be approximated with select attributes or list membership.
