Elk reclamebureau zegt dat ze cold email haten. Wij ook — totdat we doorhadden dat het probleem niet cold email zelf was, maar elk hulpmiddel dat we eraan probeerden. De generieke sjablonen. De "Hoi {voornaam}" energie. De platforms voor €300/maand die nog steeds uren handmatig werk vereisten. Dus deden we wat ontwikkelaars doen: we bouwden onze eigen.

Dit is geen theoretisch architectuurartikel. We draaien dit systeem al maanden in productie en versturen duizenden gepersonaliseerde e-mails die daadwerkelijk antwoord krijgen. Ik loop je exact langs waarom we het gebouwd hebben, hoe de onderdelen in elkaar passen en wat we op de moeilijke manier hebben geleerd.

Inhoudsopgave

Why We Built Our Own Cold Email System with Claude, Instantly & Supabase

Het probleem met standaard outreachtools

We probeerden de gebruikelijke verdachten. Lemlist. Apollo. Woodpecker. Het zijn prima tools voor veel situaties. Maar als headless web development bureau hadden onze outreachbehoeften specifieke karakteristieken waar deze platforms niet mee om konden gaan.

Dit ging steeds mis:

Generieke personalisatievelden zijn geen personalisatie. De voornaam en functie van iemand in een sjabloon invoegen overtuigt in 2025 niemand. We hadden e-mails nodig die verwezen naar de daadwerkelijke tech stack van een prospect, hun siteprestatieproblemen of specifieke architecturele beslissingen zichtbaar op hun openbare website.

De onderzoeksstap was de bottleneck. Onze best presterende outreach betrof altijd iemand van het team die daadwerkelijk de site van een prospect bekeek, deze door PageSpeed Insights haalde, hun framework controleerde en iets specifiek schreef. Dat kostte 10-15 minuten per lead. Op schaal is dat een fulltime baan.

Gegevens waren verspreid over te veel plaatsen. Leads in één spreadsheet, e-mailreeksen in een ander platform, resultaten in een derde dashboard. We konden feedback loops niet bouwen omdat niets met elkaar communiceerde.

De AI-integraties waren oppervlakkig. Sommige platforms voegden "AI-schrijven" functies toe, maar het waren vooral GPT-wrappers die dezelfde saaie kopie genereerden die iedereen anders verstuurde. Geen mogelijkheid om aangepaste context in te voeren, geen controle over prompts, geen manier om multi-staps redeneringsketens op te bouwen.

We hadden een systeem nodig waarbij AI het onderzoek deed, niet alleen het schrijven.

Onze tech stack en waarom we deze hebben gekozen

Dit is waar we na enkele iteraties landden:

Onderdeel Tool Rol Maandelijkse kosten
Leads vinden en e-mailverificatie Hunter.io E-mailadressen zoeken en verifiëren €49 (Starter)
AI-onderzoek en copywriting Claude (Anthropic API) Prospects analyseren, gepersonaliseerde e-mails genereren ~€30-60
Database en orkestratie Supabase Leads opslaan, status beheren, workflows triggeren €25 (Pro)
E-mailverzending en opwarmingsproces Instantly.ai Afleveringsvermogen, verzendinfrastructuur, opwarmingsproces €30 (Growth)
Automatiseringslijm Custom Edge Functions + Cron Alles verbinden €0 (in Supabase inbegrepen)

We evalueerden veel alternatieven. Hier is het korte verhaal waarom we gekozen hebben wat we kozen:

Claude boven GPT-4: We testtten beide uitgebreid. Claude 3.5 Sonnet (en nu Claude 4 Sonnet in 2025) produceerde consequent e-mails die natuurlijker en minder "AI-achtig" klonken. Het was ook beter in het volgen van complexe systeemprompts zonder af te dwalen. De prijzen waren vergelijkbaar, maar Claudes langere context window betekende dat we meer onderzoeksdata per prospect konden invoeren.

Supabase boven Airtable of een aangepaste Postgres-opstelling: We hadden een echte database nodig met beveiliging op rijniveau, maar wilden geen infrastructuur beheren. Supabase gaf ons Postgres, Edge Functions, Cron-jobs en een redelijk dashboard — allemaal op één plaats. We gebruiken Supabase veel voor klantprojecten, dus het team kende het al goed.

Instantly boven Lemlist of Smartlead: Het opwarmingsnetwerk van Instantly is werkelijk goed, hun API is schoon en de prijsstelling klopte voor ons volume. We hebben het ingebouwde reeksbuilder van Instantly niet nodig omdat we de sequencinglogica zelf afhandelen.

Hunter boven Apollo of Snov.io: De e-mailverificatie van Hunter is consistent het nauwkeurigst dat we hebben getest. Hun domain search API is snel en de datakwaliteit is hoog. Apollo heeft meer datapunten, maar we vonden hun e-mailnauwkeurigheid lager, wat afleveringsvermogen vermoord.

Architectuuroverzicht

Het systeem werkt in vijf fasen, die elk onafhankelijk draaien:

[Lead Sources] → [Hunter Enrichment] → [Supabase DB] → [Claude Research + Copy] → [Instantly Sending]
     ↑                                       ↑                                           |
     |                                       |                                           |
     +----------- Feedback Loop -------------+-------------------------------------------+
  1. Ingestie: We voeren prospectdomeinen uit verschillende bronnen in (handmatige lijsten, scrapers, verwijzingsgegevens)
  2. Verrijking: Hunter vindt contacten en verifieert e-mails
  3. Opslag: Alles belandt in Supabase met statustracking
  4. Onderzoek + schrijven: Claude analyseert elke prospect en genereert gepersonaliseerde kopie
  5. Verzending: Goedgekeurde e-mails gaan naar Instantly-campagnes
  6. Leren: Antwoordgegevens stromen terug naar Supabase, informeren toekomstige personalisatie

Elke fase is ontkoppeld. Als de API van Hunter uitvalt, wordt de verrijkingswachtrij gewoon vol — het breekt verzending niet. Als we Claude willen vervangen door een ander model, wijzigen we één functie.

Why We Built Our Own Cold Email System with Claude, Instantly & Supabase - architecture

Leads vinden en verrijken met Hunter

Hunter.io handelt twee kritieke taken af: de juiste persoon bij een bedrijf vinden en verifiëren dat hun e-mailadres daadwerkelijk werkt.

Hier is een vereenvoudigde versie van onze verrijkingsfunctie:

import { createClient } from '@supabase/supabase-js';

const HUNTER_API_KEY = Deno.env.get('HUNTER_API_KEY');

async function enrichLead(domain: string) {
  // Domain search om besluitvormers te vinden
  const searchRes = await fetch(
    `https://api.hunter.io/v2/domain-search?domain=${domain}&department=executive,it&api_key=${HUNTER_API_KEY}`
  );
  const searchData = await searchRes.json();
  
  const contacts = searchData.data.emails
    .filter((e: any) => e.confidence > 70)
    .slice(0, 3); // Top 3 contacten per domein
  
  // Verifieer elke e-mail
  for (const contact of contacts) {
    const verifyRes = await fetch(
      `https://api.hunter.io/v2/email-verifier?email=${contact.value}&api_key=${HUNTER_API_KEY}`
    );
    const verifyData = await verifyRes.json();
    
    if (verifyData.data.status === 'valid') {
      await supabase.from('leads').insert({
        domain,
        email: contact.value,
        first_name: contact.first_name,
        last_name: contact.last_name,
        position: contact.position,
        confidence: contact.confidence,
        status: 'enriched',
        enriched_at: new Date().toISOString()
      });
    }
  }
}

We filteren op de departementen executive en it omdat dat onze kopers zijn — CTO's, VP's Engineering, technische oprichters. De departementfiltering van Hunter is niet perfect, maar filtert veel ruis.

Een ding dat we leerden: sla e-mailverificatie nooit over. Zelfs met Hunters betrouwbaarheidsscores verifiëren we elk adres. Een bouncepercentage boven 3% vernietigt je verzenddomein reputatie. We hebben domeinen zien gaan van 95% inbox placement naar 40% spam folder door één slechte batch.

We voeren ongeveer 500 Hunter-zoekkredits per week uit, wat comfortabel in hun Starter-plan past.

AI-personalisatie met Claude

Dit is waar het interessant wordt. De Claude-integratie is niet zomaar "schrijf me een cold e-mail." Het is een meerstaps onderzoeks- en schrijfpipeline.

Stap 1: Website-analyse

Voordat Claude iets schrijft, voeren we het gegevens over de site van de prospect in. We scrapen basisinformatie met een lichte functie:

async function analyzeProspectSite(domain: string) {
  // Fetch startpagina en belangrijke pagina's
  const homepage = await fetch(`https://${domain}`);
  const html = await homepage.text();
  
  // Extract tech-signalen uit HTML
  const signals = {
    hasNextJs: html.includes('__next') || html.includes('_next/static'),
    hasReact: html.includes('react') || html.includes('__REACT'),
    hasWordPress: html.includes('wp-content') || html.includes('wp-includes'),
    hasShopify: html.includes('shopify') || html.includes('cdn.shopify'),
    hasGatsby: html.includes('gatsby'),
    usesJQuery: html.includes('jquery'),
    metaGenerator: extractMeta(html, 'generator'),
    pageSize: html.length,
    // ... meer signalen
  };
  
  // Voer PageSpeed-controle uit via API
  const psiData = await fetchPageSpeedInsights(domain);
  
  return {
    ...signals,
    performanceScore: psiData.lighthouseResult.categories.performance.score * 100,
    lcp: psiData.lighthouseResult.audits['largest-contentful-paint'].numericValue,
    cls: psiData.lighthouseResult.audits['cumulative-layout-shift'].numericValue,
    fid: psiData.lighthouseResult.audits['max-potential-fid'].numericValue
  };
}

Dit geeft Claude echte gegevens om mee te werken. Niet "Hoi, ik merkte op dat je bedrijf X doet" — meer iets als "Je homepage LCP is 4,2 seconden en je draait nog steeds jQuery naast React, wat 90KB aan je initiële bundle toevoegt."

Stap 2: Claude-onderzoeksprompt

We gebruiken Claudes API met een zorgvuldig gecrafted systeemprompt. Hier is een vereenvoudigde versie:

const researchPrompt = `Je bent een senior webontwikkelaar die de website van een prospect analyzeert voor een headless development bureau. Gegeven de volgende technische gegevens over hun site, identificeer:

1. Hun huidige tech stack (wees specifiek)
2. 2-3 concrete prestatie- of architectuurproblemen
3. Wat een migratie naar een moderne headless-architectuur zou kunnen verbeteren
4. Een specifieke, niet-voor-de-hand-liggende observatie die echte analyse toont

Wees NIET generiek. Als je iets specifiek niet kunt vinden, zeg dat dan.
Zeg NIET "in het huidige digitale landschap" of soortgelijke opvulsels.
Wees direct en technisch.

Site-gegevens:
${JSON.stringify(siteAnalysis, null, 2)}

Prospect: ${lead.first_name} ${lead.last_name}, ${lead.position} bij ${lead.domain}`;

const research = await anthropic.messages.create({
  model: 'claude-sonnet-4-20250514',
  max_tokens: 1000,
  messages: [{ role: 'user', content: researchPrompt }]
});

Stap 3: E-mailgenerering

De onderzoeksoutput voert een tweede Claude-aanroep in die de daadwerkelijke e-mail schrijft. Het scheiden van onderzoek van schrijven was een belangrijke inzicht — toen we beide in één prompt probeerden te doen, waren de e-mails slechter. Claude zou het onderzoek overslaan om sneller aan schrijven te beginnen.

const emailPrompt = `Schrijf een cold e-mail van een senior developer van een headless web bureau.

Onderzoeksnotities:
${research.content[0].text}

Regels:
- Maximaal 4-6 zinnen. Elke zin moet zijn plaats rechtvaardigen.
- Begin met de meest specifieke technische observatie.
- Geen vleierij. Geen "Ik hou ervan wat je doet."
- Één duidelijke CTA: vraag of ze een prestatie-audit willen zien.
- Klink als een developer, niet als een verkoper.
- Gebruik hun voornaam. Geen achternaam in de aanhef.
- Onderwerpregel: kort, specifiek voor hun techprobleem, kleine letters.`;

Het resultaat? E-mails die beginnen met dingen als "Je Shopify Plus-winkel rendert productpagina's die statisch gegenereerd zouden kunnen worden — dat voegt 2+ seconden toe aan elke productweergave" in plaats van "Ik merkte je indrukwekkende bedrijf op en wilde bereik."

Supabase als orkestratie-laag

Supabase is het brein van de operatie. Hier is ons kernschema:

create table leads (
  id uuid primary key default gen_random_uuid(),
  domain text not null,
  email text,
  first_name text,
  last_name text,
  position text,
  confidence int,
  status text default 'new', -- new, enriched, researched, drafted, approved, sent, replied, bounced
  site_analysis jsonb,
  research_notes text,
  email_subject text,
  email_body text,
  instantly_campaign_id text,
  sent_at timestamptz,
  opened_at timestamptz,
  replied_at timestamptz,
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

create index idx_leads_status on leads(status);
create index idx_leads_domain on leads(domain);

Het status-veld stuurt alles aan. Supabase Cron-jobs draaien elke 15 minuten, pikken leads op in elke fase en duwen ze naar de volgende:

-- Cron: Verwerk verrijkte leads door Claude-onderzoek
select cron.schedule(
  'process-research',
  '*/15 * * * *',
  $$select net.http_post(
    'https://your-project.supabase.co/functions/v1/process-research',
    '{}',
    '{"Authorization": "Bearer your-service-key"}'::jsonb
  )$$
);

We verwerken 20 leads per run in batches om binnen Claudes snelheidslimieten te blijven en de kosten voorspelbaar te houden.

De site_analysis JSONB-kolom is ongelooflijk nuttig. We kunnen over al onze leads een query stellen om patronen te vinden — zoals "toon me alle leads die WordPress gebruiken met een prestatiescores onder de 50" — en bouw gerichte campagnes vanuit die segmenten.

Verzenden op schaal met Instantly

Instantly handelt de daadwerkelijke e-mailbezorging af. We duwen goedgekeurde e-mails via hun API:

async function pushToInstantly(lead: Lead) {
  const response = await fetch('https://api.instantly.ai/api/v1/lead/add', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      api_key: INSTANTLY_API_KEY,
      campaign_id: lead.instantly_campaign_id,
      skip_if_in_workspace: true,
      leads: [{
        email: lead.email,
        first_name: lead.first_name,
        last_name: lead.last_name,
        company_name: lead.domain,
        personalization_1: lead.email_subject,
        personalization_2: lead.email_body
      }]
    })
  });
  
  if (response.ok) {
    await supabase
      .from('leads')
      .update({ status: 'sent', sent_at: new Date().toISOString() })
      .eq('id', lead.id);
  }
}

De campagnesjablonen van Instantly gebruiken variabelen {{personalization_1}} en {{personalization_2}}, die aan onze Claude-gegenereerde onderwerp en lichaam worden gekoppeld. De campagne zelf is slechts een shell — alle intelligentie woont in ons systeem.

We warmen 3 verzendaccounts op via het opwarmingsproces van Instantly voor minimaal 2 weken voordat we outreach versturen. Domeinopwarmingsproces is niet optioneel. We hebben dit op de moeilijke manier geleerd met ons eerste domein dat binnen een week werd gemarkeerd.

Afleveringsvermogen instellen

Onze verzendinfrastructuur:

  • 3 domeinen (variaties van ons merk, niet ons hoofddomein)
  • SPF, DKIM en DMARC geconfigureerd op alle domeinen
  • Google Workspace-accounts (niet Outlook — Google handelt cold email beter af in onze tests)
  • Instantly opwarmingsproces dat doorlopend draait, zelfs op actieve verzendagen
  • Maximaal 35 e-mails per account per dag
  • Willekeurige verzendintervallen van 3-7 minuten

De automatiseringslijm

Supabase Edge Functions verbinden alles. Hier is de flow in pseudocode:

Elke 15 minuten:
  1. Pak leads met status='new', voer Hunter-verrijking uit → status='enriched'
  2. Pak leads met status='enriched', voer site-analyse uit → status='analyzed'
  3. Pak leads met status='analyzed', voer Claude-onderzoek + e-mailgenerering uit → status='drafted'
  4. (Mens beoordeelt uitgewerkte e-mails in Supabase-dashboard)
  5. Pak leads met status='approved', duw naar Instantly → status='sent'
  6. Trek engagement-gegevens van Instantly API → update opened_at, replied_at

Stap 4 is belangrijk. We automatiseren verzending niet volledig. Elke e-mail wordt door een mens beoordeeld voordat deze wordt verzonden. Dit vangt de occasionele hallucinatie (Claude stelde ooit dat een site met Remix werd gebouwd toen het duidelijk Next.js was) en laat ons persoonlijke toetsen toevoegen.

De beoordelingsstap kost ongeveer 2-3 seconden per e-mail omdat Claude 95% van het werk correct doet. We keuren in batches goed met een eenvoudig Supabase-dashboard-beeld.

Resultaten en wat we hebben geleerd

We draaien dit systeem sinds Q1 2025. Hier zijn echte aantallen:

Meting Ons systeem Branche-gemiddelde (2025)
Openingspercentage 62% 24%
Antwoordpercentage 8,4% 1-3%
Positief antwoordpercentage 4,1% 0,5-1%
Bouncepercentage 0,8% 3-5%
Kosten per contact €0,18 €0,50-2,00
Tijd per lead (menselijk) ~5 seconden (beoordeling) 10-15 minuten

Het openingspercentage is hoog omdat de onderwerpen specifiek zijn. "je shopify lcp is 4,2s" wordt geopend. "Snel vraagje" niet.

Het antwoordpercentage is hoog omdat de e-mails echte technische kennis demonstreren. Wanneer een CTO een e-mail leest die correct hun tech stack identificeert en een echt prestatieprobleem, zijn ze eerder geneigd zich in te spannen — zelfs als ze weten dat het outreach is.

Wat niet werkte

Volledig geautomatiseerde verzending (geen menselijke beoordeling): We probeerden dit twee weken. Claude hallucineerde over 5% van de tijd tech stack-details. Dat is een laag foutenpercentage voor een LLM, maar een e-mail verzenden die zegt "je React-app" naar iemand die Vue draait is erger dan een generieke e-mail sturen. De vertrouwensschade is echt.

Lange e-mails: Onze eerste Claude-prompts genereerden 8-10 zin e-mails. Antwoordpercentages waren de helft van wat we nu zien met 4-6 zinnen. Korter is altijd beter.

Meer dan 40 e-mails per dag per account verzenden: Het afleveringsvermogen stort in. 30-35 is het zoete plekje in 2025.

Claude gebruiken voor vervolgacties gebaseerd op openingen: We probeerden vervolgacties te genereren geactiveerd door openingen. De vervolgacties voelden opdringerig aan en de conversie was niet het geld waard. We verzenden nu één eenvoudige, niet-AI vervolgactie drie dagen later.

Kostenanalyse

Dit kost ons maandelijks, ongeveer 2.000 leads verwerken:

Service Maandelijkse kosten Opmerkingen
Hunter.io (Starter) €49 500 zoekopdrachten + verificaties
Anthropic API (Claude) €45 ~2.000 onderzoeks- en e-mailgeneraties
Supabase (Pro) €25 Database, Edge Functions, Cron
Instantly (Growth) €30 Verzending, opwarmingsproces, analyses
Google Workspace (3 accounts) €21 Verzendinfrastructuur
Domeinen (3) €10 Jaarlijkse geamortiseerde kosten
Totaal ~€180 €0,09 per verwerkte lead

Vergelijk dat met het €79/maand plan van Apollo (beperkte verrijking, basisreeksen) of het €69/maand per zitplaats plan van Lemlist. We geven minder uit en krijgen dramatisch betere resultaten omdat de personalisatie echt is, niet op sjabloon gebaseerd.

Ter referentie: dit systeem heeft rechtstreeks leads gegenereerd die zich hebben omgezet in Next.js development en Astro development projecten ter waarde van 50-100x de maandelijkse kosten. De ROI is belachelijk hoog.

Veelgestelde vragen

Hoe lang duurde het om dit systeem te bouwen? De eerste werkende versie kostte ongeveer twee weken part-time werk — misschien 40 uur totaal. We hebben er sindsdien continue aan gewerkt, vooral het aanscherpen van Claude-prompts en het toevoegen van edge case-afhandeling. Als je comfortabel bent met Supabase Edge Functions en REST API's, zou je een basisversie in een weekend kunnen draaien.

Is dit niet zomaar spam met extra stappen? Eerlijke vraag. Het verschil is dat elke e-mail een echte technische observatie over de website van de ontvanger bevat. We blaken geen "laten we bellen" naar 10.000 mensen. We sturen specifieke, nuttige inzichten naar een gericht lijstje van mensen die daadwerkelijk de problemen hebben die we oplossen. Ons afmeldingspercentage bedraagt minder dan 0,5%, wat suggereert dat ontvangers het niet als spam zien.

Waarom Claude in plaats van GPT-4 of Gemini? We hebben alle drie getest. Claude volgte onze systeemprompts betrouwbaarder — vooral de beperkingen zoals "wees niet generiek" en "gebruik geen opvulzinnen." GPT-4 zou naar verkooptaal driften ondanks expliciete instructies dit niet te doen. Gemini was snel maar de outputkwaliteit was inconsistent. Dit kan veranderen als modellen evolueren, en ons systeem is ontworpen om modellen gemakkelijk om te schakelen.

Hoe ga je om met GDPR en CAN-SPAM-compliance? Al onze outreach richt zich op zakelijke e-mails (niet persoonlijk), bevat ons fysieke adres en heeft een duidelijke opt-out in elke e-mail. Voor GDPR verwerken we gegevens onder gerechtvaardigd belang voor B2B-outreach, onderhouden we documenten van verwerkingsactiviteiten en accepteren we verwijderingsverzoeken onmiddellijk via een geautomatiseerde webhook. We wissen ook leads ouder dan 90 dagen automatisch uit onze database. Overleg met een jurist over jouw specifieke situatie — dit is geen juridisch advies.

Wat gebeurt er wanneer een lead antwoordt? Antwoorden stromen terug van Instantly API in Supabase. We krijgen een Slack-melding voor elk antwoord, en een mens neemt het gesprek onmiddellijk over. We gebruiken AI nooit voor antwoordafhandeling. Zodra iemand zich inzet, verdienen ze een echte persoon. Geïnteresseerde prospects worden naar onze contactpagina of rechtstreeks naar een boekingslink gestuurd.

Kan deze benadering voor niet-technische diensten werken? Het site-analysedeel is specifiek voor webontwikkeling, maar het architectuurpatroon — leads verrijken, AI gebruiken voor onderzoeks- en personalisatie, versturen via een specifiek tool — werkt voor elke B2B-outreach. Je zou gewoon verschillende onderzoeksinvoer nodig hebben. Een designbureau zou visueel ontwerp en UX-patronen analyseren. Een marketingbureau zou SEO-metriek aantrekken. De sleutel is echte gegevens in Claude invoeren, niet vragen het dingen ter plekke in te vullen.

Wat is het moeilijkste deel van het onderhoud van dit systeem? Prompt-onderhoud. Als Claude-modellen updaten, hebben prompts die perfect werkten soms aanpassingen nodig. We besteden ook tijd aan het controleren van e-mailaflevering — Google Postmaster Tools controleren, spam-ratiopicken volgen, verzendaccounts roteren. Het is misschien 2-3 uur per week onderhoud totaal.

Zou je dit als product verkopen? We hebben erover nagedacht, maar eerlijk gezegd is het concurrenttievoordeel te waardevol. Als elk bureau dit exact systeem zou draaien, zou de effectiviteit dalen omdat ontvangers overal door AI onderzochte e-mails zouden gaan zien. Voorlopig houden we het als een intern tool. Als je hulp wilt met het bouwen van iets soortgelijks voor jouw bedrijf, neem contact op — we hebben enkele klanten geholpen met het opzetten van soortgelijke systemen als onderdeel van ons headless CMS development werk.