ADR-019: Magic Import
Status: Accepted Owner: @bilal Date: 2026-03-01
Context
Landlords migrating to Envo typically have property and tenant data in spreadsheets. Manual entry of 50–200+ properties is the primary adoption barrier in proptech. We need a bulk import feature that serves both tech-savvy users (exact template matching) and non-technical users (AI-assisted column mapping).
Decision
Implement a dual-mode import system with two paths:
1. Template Import (no AI)
- Download a pre-formatted CSV template
- Fill in data, upload
- Exact + alias column matching (e.g., “post code” → “postcode”, “telephone” → “phone”)
- Zero AI cost, instant processing
2. Magic Import (Claude-powered)
- Upload any spreadsheet (CSV/Excel)
- Claude Sonnet infers column mappings from headers + sample rows
- Animated agent flow (Gathering → Planning → Reasoning → Synthesising)
- User reviews and adjusts mappings before import
- ~$0.002/request, 1–2s latency
Architecture
| Component | Technology | Rationale |
|---|---|---|
| File parsing | xlsx (SheetJS) client-side | Already installed; 500 rows trivial client-side |
| Column mapping (template) | Alias lookup table | Zero latency, deterministic |
| Column mapping (magic) | Claude Sonnet via REST API | Good semantic understanding at low cost |
| Batch insert | REST API + createMany() | Single INSERT statement, ~100x faster than individual creates |
| Validation | Zod schemas (shared client/server) | Re-validation server-side for security |
Integration Points
- Onboarding: Optional step 3 (Details → Plan → Import)
- Properties page: Import button in header + empty state CTA
- Tenants page: Same pattern as properties
Two-File Import (Properties + Tenants)
- Import properties first → receive address→propertyId map
- Use map to resolve
propertyIdin tenant rows - Import tenants with resolved IDs
Scaling Path
| Phase | Scale | Approach |
|---|---|---|
| v1 (current) | ≤500 rows | Client-side parsing, single batch insert |
| v2 | ≤3,000 rows | Chunked processing (500-row batches), progress bar |
| v3 | ≤10,000 rows | Background jobs (queue + worker), email notification on completion |
Connector Framework
A connector interface is scaffolded for future integrations:
- v1: Google Sheets (stub)
- v2: Google Sheets (OAuth flow), Rightmove export
- v3: API-based connectors (property management systems)
Consequences
Positive
- Reduces onboarding friction from hours to minutes
- AI mapping handles non-standard spreadsheets gracefully
- Template mode works offline (no AI dependency)
- Connector framework enables future integration growth
Negative
- Magic import requires ANTHROPIC_API_KEY (falls back to template mode)
- 500-row limit may frustrate large operators (addressed in v2/v3)
- Client-side xlsx adds ~200KB to bundle (already installed for other features)
Risks
- AI mapping quality depends on column naming — mitigated by user review step
- Large batch inserts could timeout — mitigated by 500-row limit and chunking roadmap
Alternatives Considered
- Server-side file upload: More complex, requires file storage; 500 rows is trivial client-side
- GraphQL mutations for batch: Field selection adds overhead for bulk ops; REST is simpler
- Streaming insert (row-by-row): ~100x slower than
createMany(); no benefit at this scale