🎯 What We're Building
The Minimum Viable Product (MVP) has one job: automate the first two touchpoints of the onboarding experience — the free audit.
Specifically:
- Landing page that captures syndic email + number of buildings
- Welcome email sent instantly after sign‑up
- Automated PDF report delivered within 24h, generated from public data (IGN cadastre, PLU, solar potential)
- Follow‑up email (Day 2) with CTA to schedule a discovery call
Everything else (AG package generation, installer quotes, project management) is manual behind the scenes for now.
1. Architecture & Tech Stack
Frontend (Landing)
HTML/CSS/JS static page deployed on Netlify/Vercel
Backend (API)
Node.js + Express on Railway/Render
Database
Supabase (PostgreSQL) or Railway PostgreSQL
Email
Resend (transactional) + React Email templates
PDF Generation
Puppeteer (headless Chrome) or PDF‑Kit
Geodata APIs
IGN Géoportail API (cadastre), PVGIS (solar potential)
Why This Stack?
- Node.js – coding agents are strongest here; Puppeteer/PDF‑Kit are native.
- Supabase – includes auth, storage, real‑time; free tier generous.
- Resend – beautiful transactional emails; React‑based templates.
- Static frontend – no SSR needed; simple form → API call.
- All services have free tiers – total monthly cost: €0.
2. Project Structure
/paris‑solar‑mvp
README.md
package.json
frontend/
index.html
style.css
script.js
backend/
package.json
server.js
src/
api/
submit‑lead.js
generate‑report.js
services/
email.js
pdf‑generator.js
geodata‑scraper.js
templates/
welcome‑email.jsx
followup‑email.jsx
report‑pdf.html
scripts/
process‑queue.js (runs every 6h)
3. Data Flow
Step‑by‑step automation:
1 Lead Capture (Frontend)
// frontend/script.js
document.getElementById('audit-form').addEventListener('submit', async (e) => {
e.preventDefault();
const data = {
name: document.getElementById('name').value,
email: document.getElementById('email').value,
buildingCount: document.getElementById('building-count').value
};
const res = await fetch('https://api.solaire‑copropriete.fr/submit‑lead', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
window.location.href = '/merci.html'; // confirmation page
}
});
2 Backend Processing (Node.js)
// backend/src/api/submit‑lead.js
export async function POST(req) {
const { name, email, buildingCount } = await req.json();
// 1. Store in database
const lead = await db.insertInto('leads').values({
name, email, building_count: buildingCount,
status: 'pending', created_at: new Date()
}).returning('id');
// 2. Send welcome email immediately
await sendWelcomeEmail(email, name);
// 3. Add to processing queue (cron job every 6h)
await queue.add('generate_report', { leadId: lead.id });
return Response.json({ success: true });
}
3 Geodata Scraping (Background Job)
Problem: We need the syndic's building addresses to analyze rooftop potential.
Solution (manual MVP): Ask for addresses in follow‑up email.
Solution (automated v2): Use IGN API to find buildings by syndic name/email (requires manual mapping).
For MVP, we'll fake it:
// backend/src/services/geodata‑scraper.js
export async function generateMockReport(leadId) {
// Return mock data for 3–5 example buildings in Paris
return {
buildings: [
{
address: "123 Rue de Rivoli, 75001 Paris",
roofArea: 180, // m²
orientation: "South",
shading: "Low",
estimatedProduction: 28000, // kWh/year
annualSavings: 6500, // €
abfRestriction: true,
enedisDelay: "6‑9 months"
},
// ... more buildings
],
topThree: [/* sorted by ROI */],
summary: {
totalPotentialSavings: 18500,
buildingsWithABF: 2,
avgPaybackYears: 6.2
}
};
}
4 PDF Generation (Puppeteer)
// backend/src/services/pdf‑generator.js
import puppeteer from 'puppeteer';
export async function generatePDF(reportData, leadName) {
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
// Load HTML template with report data
const html = generateHTMLTemplate(reportData, leadName);
await page.setContent(html, { waitUntil: 'networkidle0' });
// Generate PDF
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' }
});
await browser.close();
return pdf;
}
5 Email Delivery (Resend)
// backend/src/services/email.js
import { Resend } from 'resend';
import WelcomeEmail from '../templates/welcome‑email.jsx';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function sendWelcomeEmail(to, name) {
const { data, error } = await resend.emails.send({
from: 'Audit Solaire
',
to,
subject: `Votre analyse solaire gratuite est en cours – ${name}`,
react: WelcomeEmail({ name })
});
}
export async function sendReportEmail(to, name, pdfBuffer) {
const { data, error } = await resend.emails.send({
from: 'Audit Solaire ',
to,
subject: `Votre rapport solaire personnalisé – ${name}`,
react: ReportEmail({ name }),
attachments: [{
filename: `Rapport‑Solaire‑${name}.pdf`,
content: pdfBuffer
}]
});
}
4. External APIs Required
| API | Purpose | Cost | Authentication |
| IGN Géoportail API | Cadastre data, building footprints, addresses | Free (with limits) | API key (sign‑up required) |
| PVGIS (EU JRC) | Solar radiation, production estimates | Free | None |
| OpenStreetMap Nominatim | Geocoding addresses → coordinates | Free | None (respect rate limits) |
| Google Maps JavaScript API | Interactive map in PDF (optional) | €2–10/month | API key + billing |
MVP shortcut: Use mock data for first 10 leads while we set up real API integrations.
5. What a Coding Agent Can Actually Build
✅ Realistic 48‑hour output
Given a coding agent with Node.js/Express experience:
- Hour 0–4: Project setup, dependencies, basic Express server
- Hour 4–8: Supabase connection, leads table, POST endpoint
- Hour 8–12: Resend integration, welcome‑email template
- Hour 12–16: PDF generation with Puppeteer, mock data template
- Hour 16–20: Background job (BullMQ or simple cron), report‑email
- Hour 20–24: Frontend landing page (HTML/CSS/JS)
- Hour 24–32: Deployment (Railway + Netlify), environment variables
- Hour 32–40: Testing, error handling, logging
- Hour 40–48: Documentation, README, one‑click deploy
Pre‑built components to accelerate:
- Landing page template – copy from Carrd/Webflow export
- Email templates – use React Email examples
- PDF template – HTML/CSS that Puppeteer renders
- Database schema – simple leads table + reports table
6. Deployment & Hosting
Backend
Railway.app (Node.js)
Free tier: $5 credit
Frontend
Netlify/Vercel
Free tier: 100GB bandwidth
Database
Supabase
Free tier: 500MB + 50K rows
Cron Jobs
Railway cron or GitHub Actions
Environment Variables Needed:
# .env
DATABASE_URL=postgresql://...
RESEND_API_KEY=re_...
IGN_API_KEY=ign_...
FRONTEND_URL=https://audit.solaire‑copropriete.fr
BACKEND_URL=https://api.solaire‑copropriete.fr
7. Manual Parts (For Now)
- Real geodata lookup – MVP uses mock data; v2 integrates IGN API
- AG‑document generation – manually create Word templates for first customers
- Installer quotes – email 3 installers manually, compile PDF
- Project management – Notion board + weekly email updates
Key insight: The automated audit is the hook; everything after can be manual while we validate demand.
8. Next Steps for the Coding Agent
Step 1
Set up repository
Create GitHub repo with the project structure above.
Step 2
Deploy backend
Railway.app + Supabase + Resend.
Step 3
Build landing page
Simple HTML form that posts to backend.
Step 4
Implement email flow
Welcome email → PDF generation → report email.
Step 5
Test end‑to‑end
Submit lead → receive PDF → measure open rates.
Estimated total coding time: 2–3 days for a competent agent.