Wealthbox to Insightly Migration: A Technical Guide
Technical guide to migrating from Wealthbox to Insightly — covering API constraints, field mapping, Household handling, import sequence, and the failure modes that cause silent data loss.
Planning a migration?
Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.
Schedule a free call- 1,500+ migrations completed
- Zero downtime guaranteed
- Transparent, fixed pricing
- Project success responsibility
- Post-migration support included
Wealthbox to Insightly Migration: A Technical Guide
Migrating from Wealthbox to Insightly means moving from a financial-advisor-specific CRM with Household-centric data modeling into a generalist CRM-plus-project-management platform with a fundamentally different object structure. There is no native migration path between the two systems. Wealthbox stores people, households, companies, and trusts inside its contact model; Insightly's standard model centers on Contacts and Organizations with no native Household or Trust object.
Your real options are: (1) CSV export from Wealthbox combined with Insightly's built-in CSV import for each object type, (2) a full API-to-API pipeline using Wealthbox's REST API v1 for extraction and Insightly's REST API v3.1 for loading, or (3) a hybrid approach — CSVs for flat records, API for relational data and activity history. Most migrations that need to preserve notes, opportunity-contact links, and activity timelines require at least some API work.
This guide covers the data model differences, object-by-object field mapping, API constraints on both sides, the correct import sequence, rollback procedures, and the edge cases that cause silent data loss.
If your Wealthbox workspace uses Households, Trusts, workflow templates, task attachments, or the newer Custom Objects feature, do not scope this as a "contacts import." Those are the parts that break when a team relies only on flat CSVs.
Data Model Comparison: Wealthbox vs. Insightly
The single biggest source of migration friction is the structural mismatch between these two platforms. Wealthbox is built around Contacts and Households; Insightly is built around Contacts, Organizations, Leads, and Opportunities with explicit linking.
| Wealthbox Object | Insightly Equivalent | Migration Notes |
|---|---|---|
| Contact (Person) | Contact | Direct map. Watch for Wealthbox's combined name fields vs. Insightly's FIRST_NAME / LAST_NAME split. |
| Contact (Company) | Organization | Wealthbox treats companies as contacts with a "company" type. Insightly separates them. |
| Household | Organization (or custom object) | No native Household object in Insightly. Flatten to Organization or use a custom object on Professional+. |
| Trust | Organization or custom object | No native Trust type in Insightly. Use a custom object if the trust needs its own lifecycle, or a tagged Organization. |
| Opportunity | Opportunity | Pipeline stages must be pre-configured in Insightly before import. |
| Task | Task | Wealthbox tasks link to contacts, projects, and opportunities. Insightly tasks can be linked to most objects. |
| Note | Note | Insightly notes import via a separate CSV process with strict name-matching, or via API. |
| Event | Event | Calendar events map fairly well. Recurring events may need manual recreation. |
| Project | Project | Insightly projects have pipeline stages; Wealthbox projects are simpler. |
| Workflow | Activity Set / Workflow Automation | Not directly transferable. Rebuild manually in Insightly. |
| Custom Object | Custom Object (Professional+ only) | Wealthbox's recently-added custom objects won't migrate automatically. |
| File Attachment | File Attachment | Must be handled via API. No bulk file export in Wealthbox; no file import via CSV in Insightly. |
Key differences to internalize:
- Wealthbox contacts are multi-type records. Wealthbox supports Person, Household, Company/Organization, and Trust contact records. Each can hold notes, tasks, special dates, tags, files, opportunities, and projects.
- Insightly's standard objects are different. Insightly ships with Contacts, Leads, Organizations, Opportunities, Projects, Tasks, Events, Emails, and Notes. There is no standard Household or Trust object.
- Both products support custom data models, but not on the same terms. Wealthbox's newer Custom Objects support custom fields, notes, tasks, workflows, relationships, and bulk import/update. Insightly custom objects are plan-gated: none on Plus, 25 on Professional, and 200 on Enterprise.
- Leads are first-class in Insightly, absent in Wealthbox. Insightly treats Leads separately from Contacts and supports a formal conversion flow that creates a Contact record and an Organization record when a lead is converted to an opportunity. Wealthbox has no dedicated Lead entity.
Household data requires a decision up front. Wealthbox Households are relational containers linking individual contacts (spouses, dependents) to a shared record. Insightly has no equivalent. You need to either flatten Households into Organizations with a naming convention (e.g., "Smith Household"), create a custom object on Professional+ plans, or track the relationship using Insightly's contact-to-contact linking feature.
Getting Data Out of Wealthbox
Wealthbox offers several extraction methods, but none gives you a single, clean, migration-ready package (a common pattern in financial CRMs, much like Redtail's export limitations). For a full walkthrough, see our guide: How to Export All Data from Wealthbox: Methods, Limits & Formats (2026).
Full Backup (JSON or XML)
The workspace backup gives you everything — contacts, notes, tasks, opportunities, projects, and relational links — in JSON or XML format. This is the best starting point for a programmatic migration because it preserves Household relationships and custom field data.
The Wealthbox JSON backup can easily exceed several gigabytes for established firms with 5,000+ contacts and multi-year history. Do not load this file into memory with standard scripting tools. Use a streaming JSON parser (like ijson for Python or JSONStream for Node.js) to process the file iteratively.
The JSON backup contains top-level arrays for contacts, households, notes, tasks, and opportunities. Child records (like notes) reference their parent records via internal Wealthbox IDs. You must maintain a crosswalk table mapping legacy Wealthbox IDs to new Insightly IDs during the import phase. A simple SQLite database or CSV with columns [wealthbox_id, wealthbox_type, insightly_id, insightly_type, migrated_at] is sufficient for most migrations.
Contacts CSV Export
The Contacts page export gives you a flat CSV of contact records with custom fields, but it strips out notes, tasks, opportunities, workflows, and relational Household links. Use this only if you're migrating a small contact list and don't need history.
Wealthbox REST API (v1)
The Wealthbox API is REST-based, communicates over JSON or XML, and allows you to retrieve, create, and edit contacts, tasks, events, notes, opportunities, and projects.
Key extraction details:
- Base URL:
https://api.crmworkspace.com/v1/ - Authentication: Pass an API access token as an
ACCESS_TOKENheader. OAuth 2.0 is also supported but adds complexity for a one-time migration. - Rate limit: One request per second over a five-minute sampling period, with short bursts permitted above that threshold. A
429status code is returned when you exceed the limit. Implement exponential backoff starting at 2 seconds when you receive a429. - Pagination: Always check for
nextpage links in the response and loop until exhausted. Missing pagination is the #1 cause of incomplete extractions. - Incremental extraction: Most endpoints support
updated_sincefilters, which enables delta extraction during cutover. - No webhooks: Plan for polling-based extraction.
# Fetch contacts from Wealthbox (page 1)
curl "https://api.crmworkspace.com/v1/contacts?page=1" \
-H "ACCESS_TOKEN: your_token_here" \
-H "Content-Type: application/json"User ID extraction: Wealthbox stores assigned_to as an internal user ID on most records. Before migrating contacts, extract the full user list from GET /v1/users and build a wealthbox_user_id → name/email map. You'll need this to construct the corresponding Insightly RESPONSIBLE_USER_ID crosswalk in step one of the import sequence.
One detail that's easy to miss: notes and comments are separate endpoints in the Wealthbox API. If you care about full conversation history, you need to pull both collections.
Also note that Wealthbox's public API reference still lists classic endpoints rather than explicit custom-object endpoints. If your instance uses Wealthbox Custom Objects, validate custom-object export coverage in a pilot extraction rather than assuming it behaves like contacts or opportunities.
Loading Data into Insightly
Insightly offers two ingestion paths: the built-in CSV import UI and the REST API v3.1. Which one you use depends on what you're migrating.
Insightly CSV Import
In Insightly, navigate to the target object type (Contacts, Leads, Organizations, Opportunities, or Tasks), open the … menu or top-level navigation, and select Import Data. Upload your CSV; Insightly reads column headers and attempts auto-mapping.
Constraints to know:
- File format: CSV only. UTF-8 encoding required for international characters or special symbols.
- Custom fields: Must be pre-created in Insightly before import. Attempting to import into a non-existent custom field silently drops the column.
- Activity history: Emails, calls, meetings, and file attachments cannot be imported via CSV.
- Opportunity-to-Contact linking: CSV import can link Opportunities to Organizations but not to Contacts. You'll need the API for Contact-to-Opportunity links.
- Row limits: 25,000 rows per import on Plus; 50,000 rows on Professional/Enterprise.
- Deduplication behavior — Contacts: Insightly's primary deduplication key for Contacts is email address. Two Contact rows with the same email will result in one being skipped silently. Two Contact rows with identical names but different emails will both be created. Two Contacts named "John Smith" with no email on either row will both be created — no deduplication occurs.
- Deduplication behavior — Organizations: Insightly deduplicates Organizations by name. If your file includes multiple rows with an identical organization name, Insightly creates the first record and silently skips the duplicates.
- No deduplication for Tasks or Notes: A rerun creates duplicates without warning. Always check import history before resubmitting a failed import.
Insightly Import History and Rollback
Before running any import, verify in System Settings → Import History that you can identify your imports by name and timestamp. Insightly allows you to delete an entire import batch from this screen — this is the primary rollback mechanism for CSV imports and should be your first recovery step if an import loads incorrect data.
To roll back a CSV import:
- Navigate to System Settings → Import History.
- Locate the import by name, date, and record count.
- Select Delete Import to remove all records created in that batch.
This does not undo API-created records. For API-loaded records, rollback requires either a DELETE loop using stored Insightly IDs from your crosswalk table, or a manual purge via the UI. This is why maintaining the crosswalk table with insightly_id for every created record is non-negotiable.
Insightly REST API v3.1
Insightly's REST API v3.1 uses per-user API keys over HTTP Basic authentication. It covers standard and custom CRM objects.
- Base URL:
https://api.{pod}.insightly.com/v3.1/(replace{pod}with your instance pod, e.g.,na1) - Authentication: HTTP Basic with a Base64-encoded API key as the username and an empty string as the password.
- Content type: Insightly only accepts
application/jsonas the content-type and accept header. - Rate limits: 10 requests per second; daily quotas of 40,000 (Plus), 60,000 (Professional), and 100,000 (Enterprise) requests. These limits were accurate as of the Insightly API v3.1 documentation reviewed in 2024 — verify current limits at
https://api.na1.insightly.com/v3.1before beginning. - Pagination: Returns 500 records maximum per call using OData-style
$topand$skipparameters. Default page size is 100. - Concurrency control: All response payloads include an
ETagfield. PUT requests supportIf-Matchwith the ETag value; a mismatch returns412 Precondition Failed. Use this to prevent overwriting concurrent changes during a live cutover. - No OAuth, no webhooks: Everything is polling-based.
Daily API quota planning: At 10 requests/second, the Plus plan's 40,000 daily limit is exhausted in approximately 1 hour 6 minutes of sustained throughput. A migration of 10,000 contacts with 3 notes each requires a minimum of 40,000 API calls (10,000 contact POSTs + 30,000 note POSTs) before accounting for links, tasks, or retries — which exactly hits the Plus daily ceiling with no headroom. Either upgrade temporarily to Professional (60,000/day) or spread the migration across multiple days, pausing the script when the daily quota is consumed.
import requests
import base64
import time
api_key = "your_insightly_api_key"
encoded = base64.b64encode(f"{api_key}:".encode()).decode()
headers = {
"Authorization": f"Basic {encoded}",
"Content-Type": "application/json"
}
def post_with_retry(url, payload, max_retries=5):
backoff = 5
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 201:
return response.json()
elif response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", backoff))
time.sleep(retry_after)
backoff *= 2
else:
response.raise_for_status()
raise Exception(f"Failed after {max_retries} retries")
# Create a contact in Insightly
payload = {
"FIRST_NAME": "Jane",
"LAST_NAME": "Doe",
"EMAIL_ADDRESS": "jane.doe@example.com",
"RESPONSIBLE_USER_ID": 12345, # Insightly user ID from user crosswalk
"CUSTOMFIELDS": [
{"FIELD_NAME": "Client_Type__c", "FIELD_VALUE": "Prospect"},
{"FIELD_NAME": "WEALTHBOX_ID__c", "FIELD_VALUE": "wb_67890"}
]
}
result = post_with_retry("https://api.na1.insightly.com/v3.1/Contacts", payload)
insightly_contact_id = result["CONTACT_ID"]
# Store in crosswalk: wealthbox_id=67890 → insightly_id=insightly_contact_idObject Mapping Strategy
Before writing any transformation scripts, establish a strict field-level mapping document.
Contact Field Mapping
Map Wealthbox Person contacts to Insightly Contacts. Wealthbox custom fields (e.g., Risk Tolerance, Tax Bracket) must be recreated as custom fields in Insightly prior to import.
| Wealthbox Field | Insightly Field | Notes |
|---|---|---|
first_name |
FIRST_NAME |
Direct map |
last_name |
LAST_NAME |
Direct map |
email (primary) |
EMAIL_ADDRESS |
Insightly supports multiple emails via the Emails subcollection |
phone (primary) |
PHONE |
Map by phone type: work, mobile, home |
street_address |
ADDRESS_STREET |
Insightly uses structured address fields |
city |
ADDRESS_CITY |
Direct map |
state |
ADDRESS_STATE |
Direct map |
zip |
ADDRESS_POSTCODE |
Direct map |
tags |
TAGS |
Tags can be set via PUT/POST on the parent entity. Insightly tags are case-sensitive; normalize to a consistent casing during transformation. |
contact_type |
Custom field | No native equivalent; create a custom field in Insightly |
assigned_to (user ID) |
RESPONSIBLE_USER_ID |
Requires a Wealthbox-user-ID → Insightly-user-ID crosswalk built from GET /v1/users and GET /v3.1/Users |
| Custom fields | CUSTOMFIELDS array |
When updating custom fields, you must provide the custom FIELD_NAME exactly as defined in Insightly |
Full Transformation Example: Wealthbox Contact → Insightly Contact
This snippet reads a single Wealthbox contact from the JSON backup and constructs an Insightly-formatted payload, including field mapping, name normalization, and custom field handling:
def transform_contact(wb_contact, user_crosswalk):
"""
wb_contact: dict parsed from Wealthbox JSON backup
user_crosswalk: dict mapping wealthbox_user_id -> insightly_user_id
Returns: dict ready to POST to Insightly /v3.1/Contacts
"""
# Handle combined name fields if present
first_name = wb_contact.get("first_name", "").strip()
last_name = wb_contact.get("last_name", "").strip()
if not first_name and wb_contact.get("name"):
parts = wb_contact["name"].strip().split(" ", 1)
first_name = parts[0]
last_name = parts[1] if len(parts) > 1 else ""
# Normalize tags: lowercase → title case (pick one convention and stick with it)
tags = [t.strip().title() for t in wb_contact.get("tags", [])]
# Build custom fields list
custom_fields = [
{"FIELD_NAME": "WEALTHBOX_ID__c", "FIELD_VALUE": str(wb_contact["id"])},
]
if wb_contact.get("contact_type"):
custom_fields.append({
"FIELD_NAME": "Contact_Type__c",
"FIELD_VALUE": wb_contact["contact_type"]
})
# Add financial advisor-specific fields
for wb_field, insightly_field in {
"risk_tolerance": "Risk_Tolerance__c",
"tax_bracket": "Tax_Bracket__c",
}.items():
if wb_contact.get(wb_field) is not None:
custom_fields.append({
"FIELD_NAME": insightly_field,
"FIELD_VALUE": str(wb_contact[wb_field])
})
return {
"FIRST_NAME": first_name,
"LAST_NAME": last_name,
"EMAIL_ADDRESS": wb_contact.get("email_address", ""),
"PHONE": wb_contact.get("work_phone") or wb_contact.get("mobile_phone", ""),
"ADDRESS_STREET": wb_contact.get("street_address", ""),
"ADDRESS_CITY": wb_contact.get("city", ""),
"ADDRESS_STATE": wb_contact.get("state", ""),
"ADDRESS_POSTCODE": wb_contact.get("zip", ""),
"RESPONSIBLE_USER_ID": user_crosswalk.get(wb_contact.get("assigned_to")),
"TAGS": tags,
"CUSTOMFIELDS": custom_fields,
}Households
Map Wealthbox Households to Insightly Organizations. Create a custom field on the Insightly Organization object (e.g., Record_Type) and set its value to Household to differentiate them from standard business entities. Then use the POST /v3.1/Contacts/{id}/Links endpoint to connect individual Contacts to the Household Organization.
If your target users need a true non-company entity with custom fields and controlled relationships, model the Household as an Insightly custom object instead. Insightly custom objects use lookup relationship fields rather than default two-way links, and importing into a lookup relationship field requires a strict record ID;record name format.
Pick one pattern and stick to it. Mixing approaches creates reporting headaches.
Trusts
If a trust is operationally distinct from a company — with its own lifecycle, dates, documents, owners, or opportunity connections — use a custom object if your plan allows it. If it mainly acts as a container name, a tagged Organization can work, but document the compromise explicitly. Users will otherwise assume it behaves like a native trust record and be surprised when trust-specific reporting or relationships don't work as expected.
Opportunities and Projects
- Wealthbox Opportunity → Insightly Opportunity. Both systems support multiple pipelines and stages. Ensure Insightly pipelines are configured to match Wealthbox stages before import. Imported pipeline stages will not trigger Insightly's activity sets — plan for manual follow-up or automation rebuild.
- Wealthbox Workflows → Insightly Projects. If a Wealthbox workflow tracks a complex, multi-week process (like Client Onboarding), map the parent workflow to an Insightly Project and the individual steps to Insightly Tasks linked to that Project. Wealthbox workflow history (which steps were completed, by whom, when) has no Insightly equivalent — document it separately before decommissioning Wealthbox.
- Wealthbox workflow templates cannot be exported or imported. Rebuild only the live workflows you still need in Insightly's automation model.
Tags and Segmentation
Wealthbox relies heavily on tags for segmentation. Extract the tags array from the Wealthbox JSON and include it in the TAGS array in the Insightly API payload. Normalize tag casing during transformation — Insightly tags are case-sensitive, so Client and client become two separate tags. Establish a single casing convention (e.g., Title Case) before migrating and apply it consistently across all object types.
Handling Activity History, Notes, and File Attachments
Historical context is the most valuable data in any CRM, and it's where most Wealthbox-to-Insightly migrations get stuck.
Notes
Insightly supports CSV note import, but it's a separate process that requires exact name matching. When you import notes via CSV, Insightly looks through your contacts or organizations to match the note to the correct record by matching on two fields: for contact notes, both the first name and last name must match exactly. If Insightly contacts include middle names or initials, both the first and middle names appear in the First Name field — your import CSV must replicate that combined value exactly to achieve a match.
For reliability, use the API: POST /v3.1/Notes with LINK_SUBJECT_TYPE and LINK_SUBJECT_ID to attach notes to the correct record. When parsing the Wealthbox JSON, a note will reference its parent record:
{
"id": 98765,
"body": "Discussed Q3 portfolio rebalancing.",
"linked_to": [
{ "type": "Contact", "id": 12345 }
]
}To load this into Insightly, look up the new Insightly ID for Wealthbox Contact 12345 using your crosswalk table, then POST:
{
"TITLE": "Imported Note",
"BODY": "Discussed Q3 portfolio rebalancing.",
"LINK_SUBJECT_ID": 54321,
"LINK_SUBJECT_TYPE": "Contact"
}Insightly Notes support basic HTML formatting. Wealthbox notes are typically plain text but may contain line breaks (\n). Convert bare line breaks to <br> tags when constructing the Insightly JSON payload to preserve visual formatting. Otherwise, note bodies display as a single unbroken block of text in the Insightly UI.
Emails and Call Logs
Insightly doesn't have a general-purpose "activity" import. Logged emails in Wealthbox can be converted to Insightly Notes or stored as attached text files. There is no mechanism to recreate them as native email records in Insightly's email timeline.
File Attachments
Files are the hardest component. Wealthbox does not provide bulk export for files or task attachments. The JSON backup includes file metadata (IDs, names, parent associations) but not the binary files themselves. Files attached to tasks or workflow steps are not exposed in the general Files area.
To migrate files:
- Parse the Wealthbox JSON backup to extract file IDs and their associated parent record IDs and types.
- Use the Wealthbox REST API to download each file individually:
GET /v1/files/{id}. - Store the files temporarily on an encrypted staging server. Do not use a local developer machine for files that may contain financial PII.
- Upload to Insightly using
POST /v3.1/Contacts/{id}/FileAttachments(or the equivalent endpoint for Organizations, Opportunities, etc.) with a multipart form data request.
Insightly imposes a 25 MB limit on individual file uploads via the API. Financial planning PDFs, compressed archives, or scanned documents exceeding this limit must be identified during extraction and either handled manually or stored in a document management system (SharePoint, Google Drive) with only the URL or reference metadata migrated into Insightly.
Uploaded files count against Insightly's account storage (10 GB on Plus, 100 GB on Professional, 250 GB on Enterprise). Cloud-linked files from Google Drive, Box, Dropbox, or OneDrive do not count against this limit. In practice, teams either migrate only active files or keep a searchable archive and migrate only links or metadata.
Special Dates
Wealthbox users often rely on special dates (anniversaries, advisory-specific lifecycle dates). Insightly's contact and organization imports do not include a "Dates to Remember" field. Map these to custom date fields in Insightly, handle them via an API process, or preserve them in a reporting archive. If these dates drive compliance tasks (e.g., annual review scheduling), treat them as high-priority fields and validate them in your spot-check sample before cutover.
The Correct Import Sequence
Order matters. For the best results, import Organizations before Contacts, so that contacts imported with an Organization Name column will be automatically linked.
Here's the required sequence:
- Users — Recreate all team members in Insightly first. Extract from
GET /v1/users(Wealthbox) andGET /v3.1/Users(Insightly). Build and validate the full user crosswalk before any other records are created. Record ownership is meaningless without it. - Organizations — Import Wealthbox company-type contacts, Households (as Organizations), and Trust containers first. These are your anchor records.
- Contacts — Import second. Include an Organization Name column so Insightly auto-links them. Include a
WEALTHBOX_ID__ccustom field on every record so later loads can upsert safely and rollback is traceable. - Links — Connect Contacts to Organizations using the API for any relationships the CSV auto-link missed.
- Opportunities — Import third. Include a Linked Organizations column. Contact-to-Opportunity links must be created via API afterward using
POST /v3.1/Opportunities/{id}/Links. - Projects — Import fourth. Configure pipeline stages in Insightly first or imported stages will default to the first stage.
- Tasks — Import fifth. Tasks can reference Contacts and Projects.
- Notes — Import last via API with explicit record IDs from the crosswalk. Do not rely on CSV name-matching for notes.
- Files — Upload and attach to the appropriate parent records via API after all parent records are confirmed present.
Do not import Contacts before their Organizations exist. If you import Contacts with Organization names before those container records are created, Insightly may auto-create Organizations and copy the Contact's work address and phone onto the new Organization. That's convenient for simple B2B data and a mess for Households where one person's address becomes the address of the entire household record.
Create a WEALTHBOX_ID__c custom field on every Insightly object you plan to upsert. Insightly allows searching by custom field via the API (GET /v3.1/Contacts/Search?field_name=WEALTHBOX_ID__c&field_value=wb_12345), and it does not deduplicate Task or Note imports. That single field is the difference between a repeatable pipeline and a one-way data dump you can't correct.
Rollback Procedures
Every migration needs a defined rollback path before it starts. Without one, a bad import forces a manual cleanup that takes longer than the migration itself.
Rolling Back CSV Imports
- Navigate to System Settings → Import History in Insightly.
- Locate the import by name, timestamp, and record count.
- Click Delete Import to remove all records created in that batch.
This is fast and clean for isolated imports. It does not remove records created via API in the same migration window.
Rolling Back API-Created Records
For API-loaded records, rollback is a reverse DELETE loop against your crosswalk table:
import requests
import base64
import sqlite3
api_key = "your_insightly_api_key"
encoded = base64.b64encode(f"{api_key}:".encode()).decode()
headers = {"Authorization": f"Basic {encoded}"}
conn = sqlite3.connect("crosswalk.db")
cursor = conn.cursor()
# Delete all Insightly contacts created in this migration run
cursor.execute(
"SELECT insightly_id FROM crosswalk WHERE insightly_type='Contact' AND migration_run='2024-11-15'"
)
for row in cursor.fetchall():
insightly_id = row[0]
response = requests.delete(
f"https://api.na1.insightly.com/v3.1/Contacts/{insightly_id}",
headers=headers
)
if response.status_code == 202:
cursor.execute(
"UPDATE crosswalk SET rolled_back=1 WHERE insightly_id=?", (insightly_id,)
)
conn.commit()
conn.close()Run the DELETE loop in reverse import sequence: Notes → Tasks → Files → Opportunities → Links → Contacts → Organizations. Deleting a parent Organization before deleting its linked Contacts will leave orphaned Contact records that are hard to identify later.
Partial Rollback
If only a subset of records are corrupted (e.g., one batch of notes imported against the wrong contact), use the WEALTHBOX_ID__c custom field to identify and delete only the affected records:
# Find all Insightly notes with a specific Wealthbox parent ID
GET /v3.1/Notes/Search?field_name=WEALTHBOX_PARENT_ID__c&field_value=wb_12345This is why the WEALTHBOX_ID__c field — and its equivalent on Notes and Tasks — is non-negotiable, not optional.
Insightly Plan Limits to Verify Before Migration
Before migrating, verify your Insightly plan can hold your data:
| Plan | Records | File Storage | Daily API Calls | Custom Objects |
|---|---|---|---|---|
| Plus ($29/user/mo) | 100,000 records | 10 GB | 40,000 | None |
| Professional ($49/user/mo) | 250,000 records | 100 GB | 60,000 | 25 |
| Enterprise ($99/user/mo) | 500,000 records | 250 GB | 100,000 | 200 |
Each plan also limits the number of custom fields per object type. If your Wealthbox instance uses extensive custom fields, audit the count against the Insightly plan limits before committing to a tier. The Plus plan also imposes limits on the number of custom fields per object — verify this against your field inventory before assuming Plus is sufficient for a full Wealthbox migration.
If your target Insightly plan is Plus, you have no custom objects. That single constraint often decides whether Wealthbox Households and Trusts become Organizations, custom fields on Contacts, or an external archive.
Migration Methods Compared
| Method | Best For | Preserves Relationships | Activity History | Files | Effort |
|---|---|---|---|---|---|
| CSV-only | Small datasets (<1,000 contacts), no activity history needed | Org→Contact only | ❌ Notes only (fragile) | ❌ | Low |
| API-to-API | Full migration with history, links, and attachments | ✅ Full control | ✅ As notes | ✅ Via API | High |
| Hybrid (CSV + API) | Medium datasets where core records are flat but relationships matter | ✅ | ✅ Partial | ✅ | Medium |
| Managed migration | Complex data models, tight timelines, or compliance requirements | ✅ | ✅ | ✅ | Low (outsourced) |
For Wealthbox to Insightly, the hybrid approach is usually the best balance between speed and control: bulk-load base records with CSV, then use the APIs for links, notes, delta updates, and any object that needs idempotent retries.
If all you need during cutover is net-new lead flow (not a full history move), no-code tools may be enough. Wealthbox officially documents Integrately and LeadsBridge for automation and lead sync, while Insightly officially supports Zapier on all plans and AppConnect for broader workflow automation. Those tools are appropriate for post-cutover routing or a phased sunset — not for reconstructing years of CRM history.
Common Failure Modes
Here are the traps specific to this migration pair, with the root cause and resolution pattern for each:
-
Hitting Insightly's daily API cap mid-migration. A Plus plan migration of 10,000 contacts with 3 notes each requires at minimum 40,000 API calls — exactly the daily ceiling, with zero headroom for retries. Resolution: Calculate required API calls before starting. Either upgrade temporarily to Professional or build day-boundary pausing into your migration script.
-
Name mismatches in note imports. A contact stored as "J. Robert Smith" in Wealthbox won't match "Robert Smith" in Insightly's CSV note import. Resolution: Do not use CSV for notes. Use the API with explicit record IDs from your crosswalk table.
-
Orphaned opportunity links. CSV import can't link Opportunities to Contacts. Forgetting the API link-creation step leaves Opportunities floating without contact context, invisible in contact timelines. Resolution: After Opportunity CSV import, run
POST /v3.1/Opportunities/{id}/Linksfor every Opportunity-Contact relationship in your crosswalk. -
Custom field type mismatches. Wealthbox percentage fields use natural numbers (e.g., "75" for 75%). Insightly custom fields must match the expected type and format. Resolution: Build a field-type comparison table before import. Test numeric, date, and dropdown custom fields with 10 sample records before running the full load.
-
Household flattening creates false duplicates. Converting Households to Organizations can create name collisions with real company Organizations named "Smith & Associates" vs. "Smith Household." Resolution: Use a
Record_Type__ccustom field set toHouseholdand enforce a consistent naming suffix (e.g., "— Household") to make them distinguishable in search and reports. -
Task and Note duplicate imports. Insightly does not deduplicate Task or Note imports. A blind rerun creates exact duplicates. Resolution: Store every created Insightly ID in the crosswalk table. Before inserting a Note or Task, query by
WEALTHBOX_ID__cto confirm it doesn't already exist. -
Imported pipeline stages don't trigger activity sets. Importing an Opportunity or Project with a pipeline stage does not fire that stage's Insightly activity sets. Resolution: Treat activity sets as post-migration configuration. Document which Wealthbox workflow steps mapped to which Insightly stages, then rebuild the automation rules manually.
-
Date and timezone formatting mismatches. Wealthbox dates are ISO 8601 strings but may lack explicit timezone designators. Insightly requires explicit timezones for datetime fields. Insightly's CSV note import expects dates in
DD-MMM-YYYYformat (e.g.,15-Nov-2024). Resolution: Normalize all datetime strings to UTC (2024-11-15T00:00:00Z) before transmission. For CSV note imports, convert toDD-MMM-YYYYformat in the transformation step. -
Multi-user ownership not mapped. Migrating contacts with Wealthbox
assigned_toIDs that have no corresponding Insightly user results in records with no owner, which breaks team-based filtering and task routing. Resolution: Build and validate the complete user crosswalk (GET /v1/users→GET /v3.1/Users, matched by email) before any record is migrated. Do not proceed with the contact migration until every active Wealthbox user has a confirmed Insightly counterpart. -
Compliance requirements unaddressed. The blog raises SEC/FINRA compliance as a reason to hire professionals, but the constraints are specific: SEC Rule 17a-4 requires that records be retained in a non-rewriteable, non-erasable format for defined periods (3–6 years depending on record type). FINRA Rule 4511 has parallel requirements (a strict standard that often drives firms to purpose-built financial CRMs like UGRU rather than generalist platforms). Resolution: Before decommissioning Wealthbox, confirm your retention obligations. Either archive the Wealthbox JSON backup to a WORM-compliant storage system, or verify that Insightly's data retention settings satisfy the applicable rules before the Wealthbox subscription lapses.
Validation and Cutover
Data migrations fail silently. A script that returns 200 OK for every request does not guarantee the data is usable by end users.
Before the final cutover:
- Run a subset migration first. Push 5–10% of your data into Insightly and have the operations team verify the UI before running the full load. Check that Household members are linked correctly and custom financial fields are populated.
- Verify record counts. The number of Contacts extracted from Wealthbox must match the number successfully created in Insightly. Use
GET /v3.1/Contactswith pagination to count the final volume. - Check orphaned records. Ensure no Notes or Tasks were loaded without a parent link. In Insightly, an unlinked Note is virtually impossible for a user to find through normal navigation.
- Spot-check 10% of records. Verify that Notes are attached to the correct Contacts, Opportunity values and pipeline stages are accurate, Task owners and due dates are correct, file attachments are accessible, and Household member links are intact.
- Validate user ownership. Pull a report of records where
RESPONSIBLE_USER_IDis null. Any null owner indicates an unmapped Wealthbox user ID — address before go-live.
Delta Sync at Cutover
A full API-based migration for a large firm can take 24–48 hours due to rate limits. You cannot freeze operations for two days. Execute the primary migration over the weekend, then run a delta script that queries the Wealthbox API for records where updated_since is greater than the start timestamp of your initial migration. Insightly exposes DATE_UPDATED_UTC on most major objects for sync validation on the target side. This captures any notes or tasks logged by advisors between the start of migration and the final cutover on Monday morning.
When to Skip the DIY Route
If any of the following are true, consider bringing in a migration team:
- Your Wealthbox instance has 5,000+ contacts with notes and activity history — the API rate limits on both sides make this a multi-day engineering project.
- You have Household or Trust data that needs to preserve member relationships — this requires custom transformation logic.
- You're under compliance requirements (SEC Rule 17a-4, FINRA Rule 4511) that demand audit trails and non-erasable retention for data in transit and at rest.
- Your team doesn't have a developer who can write Python/Node scripts to interact with both REST APIs, handle pagination, implement retry logic, and manage a crosswalk table.
A good migration does not pretend the two CRMs share the same shape. It preserves what matters as structured data, accepts where an archive is the safer answer, and uses repeatable source identifiers so you can retry loads without multiplying Tasks and Notes.
For a broader migration planning framework, see our CRM Data Migration Checklist.
Frequently Asked Questions
- Can I migrate from Wealthbox to Insightly using CSV files only?
- Partially. CSV import handles contacts, organizations, opportunities, and tasks, but it cannot import activity history, file attachments, or link opportunities to contacts. Insightly also does not deduplicate task or note CSV imports, so reruns create duplicates. You'll need the API for relational data and history.
- How do Wealthbox Households map to Insightly?
- Insightly has no native Household object. You can flatten Households into Organizations with a naming convention (e.g., 'Smith Household') and a Record_Type custom field, use a custom object on Professional+ plans, or track family relationships via Insightly's contact-to-contact linking feature.
- What are Insightly's API rate limits for data migration?
- Insightly enforces 10 requests per second and daily caps by plan: Plus gets 40,000, Professional 60,000, and Enterprise 100,000. At max throughput, you'd burn through the Plus daily quota in just over an hour. Plan multi-day imports for large datasets or request a temporary limit increase.
- What is the correct import order for Insightly?
- Import Organizations first, then Contacts (so they auto-link via the org name column), then Opportunities, Projects, Tasks, and Notes. Notes go last because they require exact name matching. Use the API afterward to create opportunity-to-contact links and upload file attachments.
- Can I bulk export files from Wealthbox?
- No. Wealthbox does not offer a bulk file download. Files must be downloaded individually via the REST API. Files attached to tasks or workflow steps are not exposed in the general Files area, so plan file extraction as a separate workstream.