Your Notion CRM works fine until it doesn't. At 300 contacts, queries slow. At 500, you realize you're managing relationships in a spreadsheet masquerading as a database. When you move to a real CRM, the stakes climb: a CSV export will orphan your linked records, collapse your relationship map, and leave custom fields as stray text fields with no semantic meaning. The API-first migration path takes longer upfront but preserves every connection, mapping, and context you've built. Why CSV exports collapse Notion relationships Notion stores relationships as linked database references. When you export to CSV, those connections flatten into single-cell text or disappear entirely. A contact record linked to five deals, three company references, and a custom "source" lookup loses all of it in translation. The damage cascades: Deal-to-contact links break. Deals orphan. You lose the audit trail of which contact owned which opportunity. Company hierarchies vanish. If you linked contacts to company records, those connections are gone. You'll re-enter them manually. Rollups and formulas disappear. Any field that depended on a relationship (total deal value per contact, last interaction date) becomes static or empty. Custom fields lose type information. A "source" field that was a select in Notion becomes a text string. Filtering and grouping breaks. Historical context dies. Activity logs linked to specific contacts stay in Notion. You can't migrate them. Most teams try CSV anyway because it feels faster. Then they spend three weeks manually rebuilding relationships, creating duplicate contacts, and backfilling missing deal associations. The API approach requires scripting knowledge upfront but cuts the cleanup phase to a validation checklist. The API-first architecture: Zapier vs. native integration You have two paths: Zapier (no-code, limited throughput) or a native script (code-required, full control). Zapier path: Best for under 200 contacts Zapier's Notion and Orin CRM connectors can move contacts and associate them to existing deals if you set the zaps in the right order. Constraints: Task limits. At 500 contacts × 3 relationships each, you're looking at 1500+ Zapier tasks. Zapier's ₹800/month tier runs 5,000 tasks/month; that's workable but tight. No rollback. If a zap fails midway, you'll have orphaned records in Orin and have to clean them manually. Rate limiting. Zapier throttles API calls; a full migration of 500+ contacts takes 6–12 hours of clock time. No relationship validation. Zapier will happily link a contact to a deal that doesn't exist in Orin yet, creating broken references. Zapier works well if you're migrating fewer than 200 contacts and your relationship graph is simple (contacts → deals, no multi-level company hierarchies). Native API script: Required for 300+ contacts with complex relationships Write a Python or Node.js script that: Fetches all Notion records in dependency order (companies first, then contacts, then deals). Maps Notion record IDs to Orin contact/deal/company IDs as it creates them. Creates relationships in Orin using Orin's CRM API with full type preservation. Validates counts and relationship integrity before marking the migration complete. The script takes 2–3 days to build if you know the APIs; 5–7 days if you're learning them. But once written, you can run it on a test Orin workspace first, validate everything, then run it on production. It's idempotent: if it fails, you delete the test workspace and re-run without data loss. Step-by-step: Building the migration script Step 1: Export your Notion CRM structure (not the data yet) Document your schema: which tables exist, which fields are relationships, which are custom lookups or rollups. Create a JSON config file that maps each Notion field to an Orin field and specifies relationship types. Example config: { "contacts": { "notion_table_id": "abc123", "fields": { "name": {"orin_field": "name", "type": "text"}, "company": {"orin_field": "company_id", "type": "relation"}, "source": {"orin_field": "source", "type": "select"} } }, "deals": { "notion_table_id": "def456", "fields": { "deal_name": {"orin_field": "title", "type": "text"}, "contact": {"orin_field": "contact_id", "type": "relation"}, "value": {"orin_field": "amount", "type": "number"} } } } Step 2: Pull data from Notion in dependency order Query Notion's API for all companies first, then contacts, then deals. Store the Notion ID and a mapping to the Orin ID you'll create. Use Notion's query filters to exclude archived records. Step 3: Create records in Orin and store the ID map For each Notion record, POST to Orin's CRM API to create the contact, company, or deal. Capture the Orin ID and store it in a JSON map: { "notion_id_abc123": "orin_contact_xyz789", "notion_id_def456": "orin_deal_xyz790" } This map is your lookup table for step 4. Step 4: Rebuild relationships using the ID map Once all records exist in Orin, iterate through each relationship in Notion. For every