From Spreadsheet to CRM: A Migration Guide for Bootstrapped Founders
You've been tracking customers in a Google Sheet for months. It has columns like "Email," "Name," "Plan," "Paid?," "Notes," and a few others that made sense at the time but are now inconsistent across rows. You want to move to something real, but you're worried about losing the data you've already collected.
This guide walks through the complete migration process — from cleaning up your spreadsheet data to importing it into TinyCRM and setting up ongoing automatic tracking so you never have to do this manually again.
Before you migrate: assess your spreadsheet
Before importing anything, do a quick audit of your spreadsheet. The goal is to understand what data you have and how consistent it is.
Open your sheet and check:
- Is email always present? Email is the merge key. Rows without a valid email will be skipped. Count how many rows are missing email — if it's more than a few, fix that first.
- Are emails consistent? Look for variations like trailing spaces, capitalization differences ("John@gmail.com" vs "john@gmail.com"). TinyCRM normalizes to lowercase, but it's good to know.
- What does your "paid" column look like? TinyCRM expects 'free' or 'paid' for status. If you have Y/N, True/False, or a price, you'll need to normalize it.
- What custom columns do you have? Make a list of columns beyond email/name/status. These will become params in TinyCRM.
Step 1: Clean and normalize your spreadsheet
Before exporting, standardize the data:
Normalize the status column. TinyCRM accepts 'free', 'paid', or empty (defaults to 'free'). If you have boolean values, you can use a formula to convert:
=IF(B2="Yes", "paid", "free")
=IF(B2=TRUE, "paid", "free")
=IF(B2>0, "paid", "free")Trim whitespace from emails. Select the email column and apply:
=TRIM(LOWER(A2)) # In a helper column, then paste-as-valuesRemove duplicates. In Google Sheets: Data → Data cleanup → Remove duplicates. Use the email column as the duplicate key.
Step 2: Structure your CSV for import
TinyCRM's CSV import accepts any column structure — you map columns to fields during import. But there are best practices for the column headers:
Use clear, machine-readable column names:
email,name,status,plan,source,signup_date
john@example.com,John Doe,paid,pro,product-hunt,2025-11-01
jane@example.com,Jane Smith,free,,twitter,2026-01-15
bob@example.com,Bob Jones,paid,starter,direct,2026-02-20The email column is required. Everything else is optional. Columns that don't map to name or status will be stored as params.
Step 3: Export from Google Sheets
In Google Sheets: File → Download → Comma Separated Values (.csv).
If you have multiple sheets (e.g., one per product), export each separately. You'll import each into its respective TinyCRM project.
Step 4: Import into TinyCRM
In your TinyCRM dashboard:
- Go to Import in the sidebar
- Select the project this CSV belongs to
- Upload your CSV file
- In the column matcher, map your columns:
- Email column → email
- Name column → name
- Status/paid column → status
- All other columns → params (auto-mapped by column name)
- Click Import and wait for the progress indicator
The import is idempotent — if you import the same email twice, it upserts (updates) rather than creating a duplicate. Safe to re-run if something went wrong.
Step 5: Handle multiple products
If you have customers across multiple products in separate sheets, import each CSV into its corresponding project. Customers with the same email across projects will be automatically merged into a single customer record with multiple project associations.
After importing, navigate to the main customer table to see the unified view. Customers who appear in multiple products will have multiple project badges.
Step 6: Set up ongoing automatic tracking
The import is a one-time migration. For ongoing tracking, you need the SDK so data flows in automatically. Without it, your CRM will go stale as new customers sign up.
// After migration, add to your signup flow:
import { TinyCRM } from 'tinycrm-sdk';
const tinycrm = new TinyCRM(process.env.TINYCRM_API_KEY!);
// On new user signup
await tinycrm.identify({
email: user.email,
name: user.name,
status: user.isPaid ? 'paid' : 'free',
params: { source: 'organic', plan: user.plan },
});
// New signups now appear automatically — no more manual spreadsheet updatesSee the complete SDK integration guide for the full setup including payment events and activity tracking.
Common migration issues and fixes
Issue: Invalid emails rejected. The importer validates email format. Rows with invalid emails (missing @, extra spaces, etc.) are skipped. Check the import report for rejected rows and fix them in the source data.
Issue: Status values not recognized. TinyCRM only accepts 'free' or 'paid' for status. Any other value (including empty) defaults to 'free'. If you see unexpected status assignments, check your status column values.
Issue: Duplicate customers after import. This happens when the same person uses two slightly different emails ("john@gmail.com" and "johndoe@gmail.com"). These can't be auto-merged — you'll need to manually merge them in the dashboard or decide which record to keep.
Issue: Large files timing out. The importer handles up to 10,000 rows per import. For larger datasets, split into multiple files and import sequentially.
Ready to migrate?
Start your free trial, create a project, and import your first CSV. The column matcher makes it easy.