Toda agência diz que odeia cold email. Nós também — até perceber que o problema não era cold email em si, era cada ferramenta que tentávamos usar para isso. Os templates genéricos. A energia "Oi {firstName}". As plataformas de $300/mês que ainda exigiam horas de trabalho manual. Então fizemos o que desenvolvedores fazem: construímos a nossa própria.

Este não é um post de arquitetura teórica. Estamos rodando este sistema em produção há meses, enviando milhares de emails personalizados que realmente recebem respostas. Vou te mostrar exatamente por que o construímos, como as peças se encaixam, e o que aprendemos da forma difícil.

Índice

Por Que Construímos Nosso Próprio Sistema de Cold Email com Claude, Instantly & Supabase

O Problema com as Plataformas Prontas

Tentamos os suspeitos usuais. Lemlist. Apollo. Woodpecker. São ferramentas boas para vários casos de uso. Mas como uma agência de desenvolvimento web headless, nossas necessidades de outreach eram específicas de formas que essas plataformas não conseguiam lidar.

Aqui está o que continuava quebrando:

Campos genéricos de personalização não são personalização. Inserir o nome da empresa e cargo de alguém em um template não engana ninguém em 2025. Precisávamos de emails que referenciassem a stack tecnológica real do prospect, problemas de performance do site dele, ou decisões arquiteturais específicas visíveis no website público.

O passo de pesquisa era o gargalo. Nosso melhor outreach sempre envolvia alguém do time realmente olhando o site de um prospect, rodando através do PageSpeed Insights, checando o framework, e escrevendo algo específico. Isso levava 10-15 minutos por lead. Em escala, isso é um trabalho em tempo integral.

Dados viviam em muitos lugares. Leads em uma planilha, sequências de email em outra plataforma, resultados em um terceiro dashboard. Não conseguíamos construir feedback loops porque nada conversava com nada.

As integrações de IA eram superficiais. Algumas plataformas adicionavam features de "escrita com IA", mas basicamente eram wrappers de GPT que geravam a mesma cópia bland que todos os outros estavam enviando. Sem capacidade de fornecer contexto customizado, sem controle sobre prompts, sem forma de construir cadeias de raciocínio multi-passo.

Precisávamos de um sistema onde IA fizesse a pesquisa, não apenas a escrita.

Nosso Tech Stack e Por Que o Escolhemos

Aqui está o que desembarcamos após algumas iterações:

Componente Ferramenta Função Custo Mensal
Busca de leads e verificação de email Hunter.io Encontrar e verificar endereços de email $49 (Starter)
Pesquisa com IA e copywriting Claude (Anthropic API) Analisar prospects, gerar emails personalizados ~$30-60
Database e orquestração Supabase Armazenar leads, gerenciar estado, disparar workflows $25 (Pro)
Envio de email e warmup Instantly.ai Entregabilidade, infraestrutura de envio, warmup $30 (Growth)
Cola de automação Custom Edge Functions + Cron Conectar tudo junto $0 (incluído em Supabase)

Avaliamos várias alternativas. Aqui está a versão curta do por que escolhemos o que escolhemos:

Claude sobre GPT-4: Testamos ambos extensivamente. Claude 3.5 Sonnet (e agora Claude 4 Sonnet em 2025) consistentemente produzia emails que soavam mais naturais e menos "feitos por IA". Também era melhor em seguir prompts de sistema complexos sem desviar. O preço era comparável, mas a janela de contexto mais longa do Claude significava que podíamos fornecer mais dados de pesquisa por prospect.

Supabase sobre Airtable ou um setup Postgres customizado: Precisávamos um banco de dados real com row-level security, mas não queríamos gerenciar infraestrutura. Supabase nos deu Postgres, Edge Functions, trabalhos Cron, e um dashboard decente — tudo em um lugar. Usamos Supabase muito para projetos de clientes também, então o time já conhecia bem.

Instantly sobre Lemlist ou Smartlead: A rede de warmup do Instantly é genuinamente boa, a API é limpa, e o preço fazia sentido para nosso volume. Não precisamos do construtor de sequências integrado do Instantly porque lidamos com a lógica de sequência nós mesmos.

Hunter sobre Apollo ou Snov.io: A verificação de email do Hunter é consistentemente a mais precisa que testamos. A API de busca de domínio é rápida e a qualidade dos dados é alta. Apollo tem mais pontos de dados, mas achamos sua precisão de email menor, o que mata a entregabilidade.

Visão Geral da Arquitetura

O sistema funciona em cinco estágios, cada um rodando independentemente:

[Fontes de Lead] → [Enriquecimento Hunter] → [DB Supabase] → [Pesquisa Claude + Copy] → [Envio Instantly]
     ↑                                       ↑                                           |
     |                                       |                                           |
     +----------- Loop de Feedback -----------+-------------------------------------------+
  1. Ingest: Alimentamos domains de prospects de várias fontes (listas manuais, scrapers, dados de referência)
  2. Enrich: Hunter encontra contatos e verifica emails
  3. Store: Tudo cai em Supabase com rastreamento de status
  4. Research + Write: Claude analisa cada prospect e gera copy personalizado
  5. Send: Emails aprovados vão para campanhas Instantly
  6. Learn: Dados de resposta fluem de volta para Supabase, informando personalização futura

Cada estágio é desacoplado. Se a API do Hunter cair, a fila de enriquecimento apenas acumula — não quebra o envio. Se queremos trocar Claude por um modelo diferente, mudamos uma função.

Por Que Construímos Nosso Próprio Sistema de Cold Email com Claude, Instantly & Supabase - arquitetura

Encontrando e Enriquecendo Leads com Hunter

Hunter.io lida com dois trabalhos críticos: encontrar a pessoa certa em uma empresa e verificar se o email dela realmente funciona.

Aqui está uma versão simplificada de nossa função de enriquecimento:

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

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

async function enrichLead(domain: string) {
  // Domain search para encontrar decision makers
  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 contatos por domain
  
  // Verificar cada email
  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()
      });
    }
  }
}

Filtramos pelos departamentos executive e it porque esses são nossos compradores — CTOs, VPs of Engineering, founders técnicos. A filtragem de departamento do Hunter não é perfeita, mas corta muito ruído.

Uma coisa que aprendemos: nunca pule verificação de email. Mesmo com os confidence scores do Hunter, ainda verificamos cada endereço. Uma taxa de rejeição acima de 3% vai destruir a reputação do seu domínio de envio. Vimos domínios ir de 95% inbox placement para 40% pasta de spam por causa de um lote ruim.

Rodamos cerca de 500 credits de buscas Hunter por semana, o que cabe confortavelmente no plano Starter deles.

Personalização com IA Usando Claude

É aqui que as coisas ficam interessantes. A integração com Claude não é apenas "escreve um cold email para mim". É um pipeline multi-passo de pesquisa e escrita.

Passo 1: Análise de Website

Antes do Claude escrever qualquer coisa, alimentamos dados sobre o website do prospect. Fazemos scrape de informação básica usando uma função leve:

async function analyzeProspectSite(domain: string) {
  // Fetch homepage e páginas-chave
  const homepage = await fetch(`https://${domain}`);
  const html = await homepage.text();
  
  // Extrai sinais de tech do 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,
    // ... mais sinais
  };
  
  // Roda check de PageSpeed 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
  };
}

Isso dá ao Claude dados reais com os quais trabalhar. Não "Oi, notei que sua empresa faz X" — mais como "O LCP da sua homepage é 4.2 segundos e você ainda está rodando jQuery junto com React, o que está adicionando 90KB ao seu bundle inicial."

Passo 2: Prompt de Pesquisa Claude

Usamos a API do Claude com um system prompt cuidadosamente elaborado. Aqui está uma versão simplificada:

const researchPrompt = `Você é um desenvolvedor web sênior analisando o website de um prospect para uma agência de desenvolvimento headless. Dados os seguintes dados técnicos sobre o site deles, identifique:

1. Sua stack tecnológica atual (seja específico)
2. 2-3 problemas concretos de performance ou arquitetura
3. O que uma migração para uma arquitetura headless moderna poderia melhorar
4. Uma observação específica e não-óbvia que demonstre análise genuína

NÃO seja genérico. Se não conseguir encontrar algo específico, diga isso.
NÃO mencione "na atual paisagem digital" ou preenchimento similar.
Seja direto e técnico.

Dados do site:
${JSON.stringify(siteAnalysis, null, 2)}

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

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

Passo 3: Geração de Email

O output de pesquisa alimenta uma segunda chamada Claude que escreve o email real. Separar pesquisa de escrita foi um insight chave — quando tentamos fazer ambas em um prompt, os emails eram piores. Claude pularia a pesquisa para chegar à escrita mais rápido.

const emailPrompt = `Escreva um cold email de um desenvolvedor sênior em uma agência de desenvolvimento headless.

Notas de pesquisa:
${research.content[0].text}

Regras:
- 4-6 sentences máximo. Cada frase deve justificar seu lugar.
- Comece com a observação técnica mais específica.
- Sem elogios. Sem "Amo o que vocês estão fazendo."
- Um CTA claro: pergunte se eles gostariam de ver um audit de performance.
- Soe como um desenvolvedor, não um vendedor.
- Use o primeiro nome. Sem sobrenome na saudação.
- Subject line: curta, específica ao problema de tech deles, minúscula.`;

O resultado? Emails que começam com coisas como "Sua loja Shopify Plus está server-renderizando páginas de produto que poderiam ser geradas estaticamente — isso está adicionando 2+ segundos a cada visualização de produto" em vez de "Notei sua impressionante empresa e quis entrar em contato."

Supabase como Camada de Orquestração

Supabase é o cérebro da operação. Aqui está nosso schema core:

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);

O campo status drive tudo. Trabalhos Cron do Supabase rodam a cada 15 minutos, pegando leads em cada estágio e empurrando-os para o próximo:

-- Cron: Processar leads enriquecidos através de pesquisa Claude
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
  )$$
);

Processamos em batch 20 leads por execução para ficar dentro dos limites de rate do Claude e manter custos previsíveis.

A coluna JSONB site_analysis é incrivelmente útil. Podemos fazer queries entre todos nossos leads para encontrar padrões — como "mostra-me todos os leads rodando WordPress com score de performance abaixo de 50" — e construir campanhas focadas a partir desses segmentos.

Enviando em Escala com Instantly

Instantly lida com o envio real de email. Empurramos emails aprovados via a API deles:

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);
  }
}

Templates de campanha do Instantly usam variáveis {{personalization_1}} e {{personalization_2}}, que mapeiam para nosso subject e body gerados por Claude. A campanha em si é apenas um shell — toda a inteligência vive no nosso sistema.

Rodamos 3 contas de envio através do warmup do Instantly por pelo menos 2 semanas antes de enviar qualquer outreach. Domain warmup não é opcional. Aprendemos isso da forma difícil com nosso primeiro domínio sendo sinalizado dentro de uma semana.

Setup de Entregabilidade

Nossa infraestrutura de envio:

  • 3 domínios (variações da nossa marca, não nosso domínio principal)
  • SPF, DKIM, e DMARC configurados em todos eles
  • Contas Google Workspace (não Outlook — Google lida melhor com cold email em nossos testes)
  • Warmup Instantly rodando continuamente, mesmo em dias de envio ativo
  • Máximo 35 emails por conta por dia
  • Intervalos de envio aleatório entre 3-7 minutos

A Cola de Automação

Supabase Edge Functions conectam tudo. Aqui está o fluxo em pseudocódigo:

A cada 15 minutos:
  1. Pega leads com status='new', roda enriquecimento Hunter → status='enriched'
  2. Pega leads com status='enriched', roda análise de site → status='analyzed'
  3. Pega leads com status='analyzed', roda pesquisa Claude + geração de email → status='drafted'
  4. (Humano revisa emails rascunhados no dashboard Supabase)
  5. Pega leads com status='approved', empurra para Instantly → status='sent'
  6. Puxa dados de engajamento da API Instantly → atualiza opened_at, replied_at

Passo 4 é importante. Não automatizamos completamente o envio. Cada email recebe revisão humana antes de ir embora. Isso captura a ocasional alucinação (Claude uma vez alegou que um site foi construído com Remix quando era claramente Next.js) e nos permite adicionar toques pessoais.

O passo de revisão leva cerca de 2-3 segundos por email já que Claude acerta 95% do tempo. Aprovamos em batches usando uma view simples do dashboard Supabase.

Resultados e O Que Aprendemos

Estamos rodando este sistema desde Q1 2025. Aqui estão números reais:

Métrica Nosso Sistema Média da Indústria (2025)
Taxa de Abertura 62% 24%
Taxa de Resposta 8.4% 1-3%
Taxa de Resposta Positiva 4.1% 0.5-1%
Taxa de Rejeição 0.8% 3-5%
Custo Por Lead Contatado $0.18 $0.50-2.00
Tempo Por Lead (humano) ~5 segundos (revisão) 10-15 minutos

A taxa de abertura é alta porque as subject lines são específicas. "seu shopify lcp é 4.2s" é aberto. "Quick question" não.

A taxa de resposta é alta porque os emails demonstram conhecimento técnico genuíno. Quando um CTO lê um email que identifica corretamente sua tech stack e um problema real de performance, é mais provável que se engaje — mesmo que saiba que é outreach.

O Que Não Funcionou

Envio completamente automatizado (sem revisão humana): Tentamos isso por duas semanas. Claude alucinou detalhes de tech stack cerca de 5% das vezes. É uma taxa de erro baixa para um LLM, mas enviar um email que diz "seu aplicativo React" para alguém rodando Vue é pior do que enviar um email genérico. O dano à confiança é real.

Emails longos: Nossos primeiros prompts Claude geravam emails de 8-10 frases. Taxas de resposta eram metade do que vemos agora com 4-6 frases. Mais curto é melhor. Sempre.

Enviar mais de 40 emails por dia por conta: Entregabilidade cai drasticamente. 30-35 é o sweet spot em 2025.

Usar Claude para follow-ups baseados em aberturas: Tentamos gerar emails de follow-up acionados por aberturas. Os follow-ups pareciam agressivos e a conversão não valia o custo. Agora enviamos um simples follow-up não-IA três dias depois.

Breakdown de Custos

Aqui está o que isso custa mensalmente, processando cerca de 2,000 leads:

Serviço Custo Mensal Notas
Hunter.io (Starter) $49 500 buscas + verificações
Anthropic API (Claude) $45 ~2,000 gerações de pesquisa + email
Supabase (Pro) $25 Database, Edge Functions, Cron
Instantly (Growth) $30 Envio, warmup, analytics
Google Workspace (3 contas) $21 Infraestrutura de envio
Domínios (3) $10 Custo anual amortizado
Total ~$180 $0.09 por lead processado

Compare com o plano $79/mês do Apollo (enriquecimento limitado, sequências básicas) ou $69/mês por seat do Lemlist. Estamos gastando menos e conseguindo resultados dramaticamente melhores porque a personalização é real, não baseada em template.

Para contexto, este sistema diretamente gerou leads que viraram projetos de desenvolvimento Next.js e desenvolvimento Astro valendo 50-100x o custo mensal. O ROI é absurdo.

FAQ

Quanto tempo levou para construir este sistema?

A primeira versão que funcionava levou cerca de duas semanas de esforço part-time — talvez 40 horas no total. Temos iterado continuamente desde então, principalmente ajustando prompts Claude e adicionando tratamento de casos extremos. Se você está confortável com Supabase Edge Functions e REST APIs, poderia ter uma versão básica rodando em um fim de semana.

Isso não é apenas spam com passos extras?

Pergunta justa. A diferença é que cada email contém uma observação técnica genuína sobre o website do destinatário. Não estamos disparando "vamos agendar uma call" para 10,000 pessoas. Estamos enviando insights específicos e úteis para uma lista focada de pessoas que realmente têm os problemas que resolvemos. Nossa taxa de unsubscribe é abaixo de 0.5%, o que sugere que recipients não o veem como spam.

Por que Claude em vez de GPT-4 ou Gemini?

Testamos todos os três. Claude seguiu nossos system prompts mais confiável — especialmente as restrições como "não seja genérico" e "não use frases de preenchimento". GPT-4 derivaria para linguagem de vendas mesmo com instruções explícitas contra. Gemini era rápido mas a qualidade de output era inconsistente. Isso pode mudar conforme modelos evoluem, e nosso sistema é projetado para trocar modelos facilmente.

Como vocês lidam com compliance GDPR e CAN-SPAM?

Todos nossos outreach alvo business emails (não pessoais), inclui nosso endereço físico, e tem um clear opt-out em cada email. Para GDPR, processamos dados sob legitimate interest para outreach B2B, mantemos registros de atividades de processamento, e honramos pedidos de remoção imediatamente via um webhook automatizado. Também purgamos leads com mais de 90 dias automaticamente da nossa database. Fale com um advogado para sua situação específica — isso não é aconselhamento legal.

O que acontece quando um lead responde?

Respostas fluem de volta da API do Instantly para Supabase. Recebemos notificação no Slack para cada resposta, e um humano assume a conversa imediatamente. Nunca usamos IA para handling de respostas. Uma vez que alguém se engaja, merece uma pessoa real. Prospects interessados são apontados para nossa página de contato ou diretamente para um link de agendamento de call.

Esta abordagem pode funcionar para serviços não-técnicos?

A peça de análise de site é específica para desenvolvimento web, mas o padrão de arquitetura — enriquecer leads, usar IA para pesquisar e personalizar, enviar através de uma ferramenta dedicada — funciona para qualquer outreach B2B. Você apenas precisaria de inputs de pesquisa diferentes. Uma agência de design poderia analisar padrões de visual design e UX. Uma agência de marketing poderia puxar métricas SEO. A chave é alimentar Claude dados reais, não pedir que ele invente coisas.

Qual é a parte mais difícil de manter este sistema?

Manutenção de prompt. Conforme modelos Claude atualizam, prompts que funcionavam perfeitamente às vezes precisam ajuste. Também passamos tempo monitorando entregabilidade de email — checando Google Postmaster Tools, observando spikes de taxa de spam, rodando contas de envio. É talvez 2-3 horas por semana de manutenção total.

Vocês venderiam isso como um produto?

Pensamos nisso, mas honestamente a vantagem competitiva é muito valiosa. Se cada agência rodasse este sistema exato, a efetividade cairia porque recipients começariam a ver emails pesquisados por IA em todo lugar. Por enquanto, estamos mantendo como uma ferramenta interna. Se você quer ajuda construindo algo similar para seu negócio, entre em contato — ajudamos alguns clientes a configurar sistemas similares como parte do nosso desenvolvimento de headless CMS.