Lemlist costa 60€/mese. Instantly 97€/mese. Smartlead 39€/mese ma con limiti pesanti sul tier base. Io faccio outreach B2B su volumi piccoli (40-60 invii/giorno), ho un VPS Hostinger gia attivo per altri progetti e so scrivere abbastanza Next.js da non avere paura di un'app full-stack. Cosi ho costruito sisofo-outreach: la mia versione di un tool di cold email outreach open source, self-hosted, GDPR-friendly, che gira a meno di 10 euro al mese.
Questo articolo e un case study tecnico, scritto developer-to-developer. Stack, architettura, schema DB, endpoint principali, anti-spam thresholds, numeri reali su 337 invii e roadmap delle cose che non ho ancora fatto. Niente vendita di servizio: se ti serve la versione tua, in fondo trovi le opzioni.
Perche farsi un tool outreach in casa nel 2026
Le SaaS outreach mature (Lemlist, Instantly, Smartlead, Apollo) sono ottime se mandi 1.000+ email/giorno o se hai un team commerciale che vive dentro al tool. Per uno sviluppatore freelance o una micro-PMI ci sono tre motivi seri per costruire in casa:
- Controllo dati GDPR: i prospect vivono in tabelle Postgres che possiedo io, non su server USA. Quando un destinatario chiede cancellazione ex art. 17 GDPR, ho una query SQL diretta — non un ticket di support che risponde in 5 giorni.
- Costi predicibili:Lemlist + tool di enrichment + warm-up arrivano facilmente a 150€/mese. Il mio setup costa meno di 10€/mese reali (vedremo i numeri sotto). Su 12 mesi sono circa 1.700€ di differenza.
- Custom logic:nessuna SaaS mi lascia definire kill switch su bounce rate < 8% e spam complaint rate < 0.3% calcolati su finestra mobile di 25 invii. Io l'ho codificato in 80 righe di SQL + un cron.
Per chi e curioso di come trasformo questi stessi pattern in soluzioni custom per clienti, ho una sezione dedicata su servizi di automazione per PMI.
Stack scelto e perche
Volevo zero esoterismi. Strumenti che conosco gia, deploy banale, costi quasi a zero. Risultato:
- Next.js 16 per UI dashboard + API routes. App Router, Server Components dove ha senso, Route Handlers per webhook e cron. Niente Vercel: deploy Docker su VPS.
- Supabase come Postgres managed + auth. EU region, RLS attiva, dashboard SQL utilissima per debug. Free tier piu che sufficiente per 5.000 prospect e qualche centinaio di migliaia di event row. Riferimento doc: supabase.com.
- Brevo come email provider, ma via HTTP API (NON SMTP). Il transactional endpoint e ben documentato, supporta tracking pixel, webhook event push, gestione automatica list-unsubscribe header. Free tier 300 email/giorno. Riferimento doc: brevo.com.
- Docker + Docker Composesul VPS Hostinger. Un container Next.js, un container Caddy davanti come reverse proxy con cert Let's Encrypt automatico.
- Cron host (NON cron Vercel) per scheduling invii ogni 10 minuti dalle 9:30 alle 17:30 nei giorni feriali. Setup tramite uno script
setup-vps-cron.shche chiama gli endpoint/api/cron/...con un bearer token.
Perche NON Vercel cron
Vercel cron e ottimo, ma su piano Hobby il limite e 2 cron job/giorno con 60 secondi di timeout per chiamata. Io ne voglio uno che giri ogni 10 minuti in fascia oraria business, piu uno notturno per scoring e cleanup. Su VPS un crontab classico costa zero e non ha limiti di esecuzione. Stessa logica funziona anche per chi gira n8n self-hosted.
Architettura
Schema DB (5 tabelle)
Tutto Postgres su Supabase, niente NoSQL, niente ORM pesanti — query SQL native con il client Supabase JS.
outreach_campaigns: una riga per campagna (nome, oggetto, daily cap, status: active/paused/killed, timestamp).outreach_prospects: lead importati con email, dominio, persona, segmento, MX validato, campagna, stage corrente (cold, follow1, follow3, replied, unsubscribed, bounced).email_templates: i body markdown + oggetto per ogni stage di ogni campagna (cold, follow1, follow3). Versionati per A/B futuro.email_queue: lo stato di ogni email pianificata. Status flow: pending → sending → sent → (opened|clicked|bounced|spam|unsubscribed).email_events: log immutabile di ogni event dal webhook Brevo, raw payload + tipo evento normalizzato. Append-only.unsubscribe_log: append-only di ogni richiesta di disiscrizione, con timestamp, source (header List-Unsubscribe, link footer, reply manuale), email hashed.
Sei tabelle in totale (le 5 promesse nell'intro + unsubscribe_log come append-only). Indice composito su(campaign_id, status) e (prospect_id, stage) per i due hot path.
Endpoint principali
POST /api/cron/send-next— chiamato dal cron host ogni 10 minuti. Pesca la prima emailpendingrispettando il daily cap e il rhythm, controlla anti-spam threshold della campagna, chiama Brevo HTTP API, marcasent.POST /api/webhooks/brevo— riceve event push da Brevo (delivered, opened, clicked, bounced, spam, unsubscribed, inbound_email), normalizza i payload e aggiornaemail_queue+email_events.GET /api/cron/status— report giornaliero: invii della giornata, open rate ultimi 7 giorni, bounce rate, spam complaint rate, prospect rimasti. Restituisce JSON che viene mandato a Telegram dal cron stesso.POST /api/unsubscribe— endpoint pubblico, idempotente. Riceve il token firmato dal link unsubscribe in fondo all'email, scrive suunsubscribe_log, marca il prospect.POST /api/indexnow-ping— chiamato dopo deploy per notificare Bing/Yandex sulle nuove pagine pubblicate (utile perche uso lo stesso VPS anche per altri siti).
Cron host vs Vercel cron (perche VPS)
Tre vantaggi pratici del cron VPS classico per questo caso:
- Granularita: posso schedulare ogni 10 minuti senza pagare un piano superiore.
- Time zone: Europe/Rome nativa, niente conversioni UTC sbagliate sui follow-up.
- Debug:
tail -f /var/log/cron.loge ho tutto sotto mano, senza dover passare dalla dashboard del provider.
Funzionalita chiave realizzate
Cold + follow1 + follow3 con DAG temporale
Ogni prospect attraversa un mini-DAG (Directed Acyclic Graph): cold → attesa 3 giorni → follow1 (se non ha aperto/risposto) → attesa 5 giorni → follow3 (se ancora silenzio). Lo stage corrente e una colonna sul prospect, le transizioni sono gestite da un job notturno che inserisce nuove righe in email_queue con scheduled_at calcolato.
Vantaggio rispetto a una FSM hardcoded: posso aggiungere un follow4 o un cambio di tempo modificando solo lo scheduler, senza toccare la logica di send.
Webhook Brevo normalizzato
Brevo manda eventi separati per delivered, opened, uniqueOpened, proxyOpen, clicked, bounced (soft + hard), spam, unsubscribed, inbound_email. Nel webhook li normalizzo in un tipo interno piu pulito:
delivered→sentconfermato (consegnato al mailbox provider).opened+uniqueOpened+proxyOpentutti raggruppati sottoopen, ma il flagis_proxy_openresta per filtraggio (i proxy open di Apple Mail Privacy Protection vanno scartati nel calcolo open rate reale).bouncedhard e soft trattati diversamente: hard bounce blocca subito il prospect, soft bounce ammette 2 retry.inbound_emaile la reply del destinatario: scatta auto-pause dei follow-up successivi e notifica Telegram.
Anti-spam thresholds + kill switch
Il calcolo gira a ogni send-next: prendo gli ultimi 25 invii della campagna, calcolo bounce rate e spam complaint rate. Se bounce >= 8% o spam >= 0.3%, marco la campagna killed e mando alert Telegram. Soglie ispirate a regole interne di Gmail/Outlook viste in pratica negli ultimi due anni.
Importante: la query e filtrata per campagna, non globale. Una campagna con liste sporche non deve uccidere una campagna ben targettizzata. Bisogna anche distinguere “dato sporco” (importazione pessima) da “reputation problem” (qualcosa non va col dominio): nel primo caso si pulisce la lista, nel secondo si ferma tutto.
Unsubscribe automatico + GDPR
Ogni email ha tre meccanismi di unsubscribe attivi insieme:
- Header
List-UnsubscribeeList-Unsubscribe-Post: List-Unsubscribe=One-Click(RFC 8058), che attiva il pulsante nativo di Gmail/Apple Mail. - Link unsubscribe testuale nel footer, con token HMAC firmato che identifica prospect + campagna.
- Parsing automatico delle reply che contengono parole chiave (“unsubscribe”, “rimuovi”, “cancellami”, “non sono interessato”).
Per realta italiane che vogliono usare l'app per outreach legale ci sono altre considerazioni (informativa, base giuridica del trattamento, retention): ne ho parlato sul piano operativo nella mia pagina consulente AI e automazione a Roma.
Numeri reali (stima osservata su 337 invii)
Settimana di osservazione attuale, campagna “Agenzie Roma”:
- Invii totali: 337 in 7 giorni (cold + follow-up).
- Open rate: circa 6.2% — basso ma realistico per cold B2B IT verso liste fredde non-warm-up. La media dichiarata dai vendor SaaS (20-30%) si ottiene con warm-up dedicato e liste verificate, non da freddo puro.
- Spam complaint rate: 0% — effetto del filtro MX pre-import, dell'header List-Unsubscribe e dei rhythm bassi.
- Hard bounce rate: 0% sopra threshold critico — merito della MX validation fatta prima ancora di importare il prospect.
- Reply rate utile: 4 reply su 337 invii, di cui 1 lead qualificato. Numero piccolo, ma in linea con quello che ci si aspetta da volumi bassi e copy non aggressivo.
Disclaimer onesto: 337 invii sono pochi per parlare di benchmark. Sono la fotografia reale di una settimana di lavoro, utile per chi vuole capire ordini di grandezza prima di mettere in piedi qualcosa di simile.
Costi mensili reali
- VPS Hostinger: 6.99€/mese (1 vCPU, 4 GB RAM, 100 GB SSD). Condiviso con altri progetti, quindi il costo “solo outreach” e una frazione.
- Supabase free tier: 0€/mese fino a 500 MB DB e 2 GB di transfer. Non lo sforo.
- Brevo free tier: 0€/mese fino a 300 email/giorno transactional. Non lo sforo.
- Dominio email secondario: circa 12€/anno = 1€/mese ammortizzato.
- Telegram bot per alert: 0€/mese.
Totale: meno di 10€/mese. Contro 60€/mese di Lemlist Starter, su 12 mesi parliamo di circa 600€ risparmiati. Il break-even sul tempo di sviluppo iniziale (circa 30 ore di codice fra UI e backend) lo recupero in poco piu di un anno se conto il costo opportunita reale di una micro-impresa.
Cosa NON ho ancora fatto (roadmap pubblica)
Sezione onesta: le cose che mancano e che vorrei aggiungere prossimamente.
- A/B testing template: due varianti di oggetto/body sulla stessa campagna con split casuale e calcolo automatico del vincitore dopo N invii. Logica gia pensata, manca la UI.
- AI subject generator: chiamata a Claude Haiku 4.5 per generare 5 oggetti diversi a partire da prospect + offer, con score di previsione open rate. Costo stimato sotto i 2€/mese.
- Calendario booking integrato: link Cal.com nel template con tracking automatico delle prenotazioni come conversion event.
- Inbound IMAP fetch: oggi le reply le prende il webhook Brevo (inbound_email), ma vorrei un fallback IMAP poll per il caso in cui qualcuno risponda a una email piu vecchia di 90 giorni (oltre la retention webhook).
- Multi-inbox rotation: rotazione automatica su 2-3 sender per distribuire reputation e alzare il daily cap totale.
Vuoi una versione per te?
Il codice oggi e in preview privato. Non c'e ancora un repo GitHub pubblico, ma sto valutando di rilasciarlo open source su licenza permissiva (MIT o AGPL) entro fine 2026, quando avro chiuso A/B testing e AI subject generator. Nel frattempo due opzioni concrete:
- Open source su richiesta: se sei uno sviluppatore e vuoi vedere come e fatto dentro per ispirazione, mandami una mail a info@andreasisofo.it con due righe sul tuo use case. Condivido architettura piu in dettaglio e, se ha senso, accesso al repo privato.
- Build su misura: se vuoi che lo adatti al tuo workflow (CRM esistente, gestionale custom, dominio multi-tenant), lavoro su progetti del genere — vedi /consulente-ai-roma e /servizi/automazione.
FAQ
Perche non usi Lemlist?
Costo (60€/mese contro 10€), controllo dati (server EU che possiedo io), e customizzazione (kill switch e thresholds modellati sul mio workflow). Sotto i 500 invii/giorno, per uno sviluppatore, il rapporto effort/benefit del self-hosted vince.
Come gestisci compliance GDPR italiana?
Tre layer: base giuridica documentata (legittimo interesse B2B con LIA scritta), unsubscribe automatico in ogni email (header + link), dati su Supabase EU region con DPA firmato.
Quanti contatti riesco a mandare al giorno con Brevo free?
300 transactional/giorno sul free tier — piu che sufficienti per outreach ragionato. Sopra serve Brevo Starter (circa 25€/mese, 20.000 email/mese).
Posso usare un dominio diverso da quello del sito?
Si, anzi e raccomandato. Dominio secondario dedicato con SPF/DKIM/DMARC propri, warm-up graduale di 4-6 settimane, dominio principale solo per email transactional/personali.
Come monitoro la reputazione del dominio?
Tre strumenti gratuiti settimanali: Google Postmaster Tools, Microsoft SNDS, mxtoolbox.com. Lato app, il kill switch interno gira su finestra mobile di 25 invii per campagna.
Conclusione
Non ho costruito sisofo-outreach per dimostrare di poter sostituire Lemlist. L'ho costruito perche, dato il mio profilo (volumi piccoli, controllo dati maniacale, gia VPS attivo, abbastanza confidenza con Next.js), il bilancio costi/benefici era ovviamente a favore del self-hosted. Per altri profili (team commerciale, 5.000 email/giorno, zero voglia di toccare codice) la risposta giusta resta la SaaS.
Se vuoi vedere lo stesso approccio applicato a casi piu “hardware-on”, il case study su Sofia, agente AI vocale per una trattoria romana racconta come dietro a un tool che “funziona” ci sia sempre molto piu lavoro di plumbing di quanto si pensi.
Vuoi vedere il codice dentro o farti la tua versione?
Sono in preview privato. Due opzioni: ti do accesso per ispirazione (mandami una mail con il tuo use case) oppure costruisco una versione adattata al tuo workflow.
Parliamone →
