Guia para equipes com e-mails no código: faça o inventário com um agente de programação, reúna tudo num canvas do Brew via MCP, migre e melhore com segurança.

E-mails no código são e-mails do produto definidos na sua base de código e lançados como qualquer outra funcionalidade. Numa equipe que usa Resend e React Email, pergunte quais e-mails o produto envia e a resposta demora a vir. O e-mail de boas-vindas é um componente em emails/. A redefinição de senha fica no painel do provedor de autenticação. O e-mail de fim do período de teste é uma string em um cron job. O Stripe envia o recibo. Ninguém lembra o que envia o e-mail “sentimos a sua falta”.
Cada e-mail foi fácil de lançar, então ninguém tem a visão completa. Nós resolveríamos isso nesta ordem: visibilidade, depois migração, depois melhoria. O agente de programação que você já usa, conectado ao Brew via MCP, consegue fazer a maior parte do trabalho.
import_email. A importação é determinística, não usa IA e é gratuita.O React Email e o Resend tornaram o e-mail muito melhor para os desenvolvedores: componentes tipados, revisados em pull requests. O custo aparece depois.
Os e-mails ficam espalhados. Componentes, HTML inline em uma tarefa em segundo plano, modelos no painel de um terceiro, e-mails que outro serviço envia por você. Nenhum arquivo lista todos eles.
A prévia não é a produção. O servidor de prévia do React Email mostra um modelo com as props de prévia dele, não o que o cliente recebeu, e nada dos outros modelos.
Só os engenheiros conseguem ver. Produto, design e suporte teriam que pedir a um engenheiro para rodar o projeto, então quase nunca pedem.
Nada é revisado depois do lançamento. O e-mail de dois anos atrás ainda tem o logo antigo e um plano que você não vende mais. Ninguém percebe, porque ninguém vê os e-mails juntos.
O objetivo é ter uma visão compartilhada de todos os e-mails. Nada aqui mexe na produção.

Cada fase é útil por si só, e você pode parar depois de qualquer uma delas.
O servidor MCP do Brew fica em https://brew.new/api/mcp e usa OAuth: você faz login no Brew e escolhe a marca em que o agente pode trabalhar. No Claude Code:
claude mcp add --transport http brew "https://brew.new/api/mcp"O comando só salva o servidor na configuração local deste projeto. Para usá-lo em todos os projetos, adicione --scope user. Antes de usar as ferramentas, execute claude mcp login brew ou abra /mcp e autentique a conexão do Brew.
A documentação de configuração dos clientes tem instruções para o Cursor e o Codex. Clientes sem OAuth podem usar uma chave de API restrita à marca. Depois, peça ao agente que chame get_brew_capabilities, para que ele leia a lista de ferramentas atual em vez de adivinhar.
Peça uma lista ao agente:
Find every email this codebase sends. Search for provider calls
(resend.emails.send, postmark, sendgrid, nodemailer, SES), React Email
components and render() calls, and any email templates referenced from
config or a third-party dashboard.
For each email, record: a name, what triggers it, the file and line that
sends it, the template file, the provider, and the variables it uses.
Write the result as a table in EMAILS.md. List anything you could not
resolve under "Unknown" instead of guessing.Mantenha a seção “Unknown” (o que o agente não conseguiu identificar). As confirmações de autenticação e os recibos do Stripe não são chamadas de envio no seu código, mas fazem parte da lista. Faça o commit do EMAILS.md. Se você parar aqui, ainda terá uma lista que não tinha ontem.
A melhor entrada para a importação é o HTML que o seu código já produz. O utilitário render do React Email devolve essa string:
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'
const html = await render(
<WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)Projetos mais antigos importam render de @react-email/render. Use dados de exemplo realistas, e não "test", já que a sua equipe vai revisar esses e-mails. Peça ao agente um script que renderize cada modelo em um arquivo.
O Brew também importa diretamente um módulo do React Email com format: "jsx", lendo-o de forma estática, sem executá-lo. As props assumem os valores padrão ou os PreviewProps, componentes importados de outros arquivos locais são descartados ou rejeitados, e await e recursos parecidos são recusados. Para qualquer coisa além de um único arquivo autossuficiente, use o HTML renderizado.
Depois, importe cada renderização com import_email, com um groupName por área do produto:
For each rendered email in ./email-renders, call import_email with
format "html", the title and subject line from EMAILS.md, and a
groupName by area: "Auth", "Onboarding", "Billing" or "Lifecycle".
Report the emailId for each one and any warnings in its assetReport.Os desenvolvedores perguntam, com razão, se a importação reescreve os e-mails deles. Não reescreve:
assetReport lista as que não conseguiu buscar.baseUrl só resolve caminhos relativos de imagens.Cada grupo ganha um canvas. Abra brew.new/emails/onboarding e a sequência inteira aparece lado a lado, com uma alternância entre desktop e celular e comentários em cada design. Os links do canvas funcionam para quem está conectado ao seu espaço de trabalho do Brew. Não há link de compartilhamento público, então convide quem vai revisar.
É aqui que os problemas aparecem: dois logos, um botão com link quebrado, três rodapés. Rode o audit_email antes de corrigir qualquer coisa. Ele verifica links, imagens, conformidade, tamanho carregado, compatibilidade com os clientes de e-mail, acessibilidade e redação, custa 5 créditos por auditoria completa e aceita um emailId salvo ou HTML puro, então você pode auditar e-mails que ainda não importou. O nosso checklist de QA antes do envio explica cada verificação e cada limite.
Agora você tem um inventário, todos os e-mails em um canvas e uma lista de problemas, e o seu código continua enviando tudo como antes.
Agora o Brew passa a enviar alguns e-mails no lugar do seu código. Nem todo e-mail precisa migrar, e só um sistema envia cada tipo de e-mail em um dado momento.
| No seu código hoje | No Brew |
|---|---|
resend.emails.send({ react: <WelcomeEmail /> }) |
Um evento de gatilho mais uma automação publicada com uma etapa Enviar e-mail |
| Props passadas para o componente | Campos do payload do gatilho, usados como tags de mesclagem, como {{ firstName | there }} ou {{ trigger.planName }} |
O domínio do from |
Um domínio de envio verificado no Brew |
| A sua tabela de usuários | Contatos, criados pela API REST ou por uma integração |
| Um cron job que envia uma sequência | Uma automação com etapas Aguardar, Filtro e Enviar e-mail |
O agente chama create_domain para obter os registros DNS e depois verify_domain, quando os registros estiverem publicados. Domínios criados pelo MCP e pela API são domínios de marketing. Para redefinições de senha e recibos, configure no app do Brew um domínio com finalidade transacional: ele dispensa o rodapé de descadastro e entrega para contatos descadastrados. A documentação explica os registros em como verificar o seu domínio de envio.
O MCP atualiza contatos, mas não os cria. Crie-os a partir do seu back-end com POST /v1/contacts, que cria ou atualiza um contato ou um lote de até 1.000 e cria os campos personalizados automaticamente, ou faça upload de um CSV no app. Disparar um gatilho também cria ou atualiza o contato que está no payload. Só importe pessoas que concordaram em receber os seus e-mails. A página sobre como adicionar contatos explica o que o Brew espera.
É fácil deixar passar: o e-mail de boas-vindas importado diz literalmente “Olá, Ana”. Substitua cada valor de exemplo por uma tag de mesclagem, como {{ firstName | there }}, em que o texto depois da barra é o valor padrão. Use o editor do Brew ou peça ao agente que rode o edit_email limitado a essas substituições. É uma edição feita por IA, que consome créditos, então confira a prévia. A página sobre tags de mesclagem traz a sintaxe.
O agente cria um gatilho com save_trigger. O esquema do payload precisa incluir o email do destinatário. Dê ao gatilho um contrato tipado (o infer_payload_contract rascunha um a partir de um payload de exemplo, e o set_trigger_contract o salva) e depois busque-o com get_trigger_contract no formato ts ou zod, para que o seu back-end e o Brew concordem sobre o formato do payload.
Em seguida, o save_automation monta o gatilho mais uma etapa Enviar e-mail, com o e-mail, o domínio e o assunto. A automação continua como rascunho até você publicar. O dryRun valida o grafo, o test_automation envia uma cópia real para o seu endereço e o check_trigger confirma que uma automação publicada está escutando antes que a produção dispare qualquer coisa.
Esta é a única mudança no código da sua aplicação. Antes, com o Resend:
await resend.emails.send({
from: 'Acme <hello@acme.com>',
to: user.email,
subject: 'Welcome to Acme',
react: <WelcomeEmail firstName={user.firstName} planName={plan.name} />,
})Depois, com o Brew:
const res = await fetch(
`https://brew.new/api/v1/automations/triggers/${process.env.BREW_WELCOME_TRIGGER_ID}/fire`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BREW_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `welcome-${user.id}`,
},
body: JSON.stringify({
payload: { email: user.email, firstName: user.firstName, planName: plan.name },
}),
}
)
if (!res.ok) {
// 422 means the automation is still a draft; 400 means the payload failed the schema.
throw new Error(`Brew trigger failed: ${res.status} ${await res.text()}`)
}O fetch resolve mesmo quando a requisição é rejeitada, então verifique res.ok e deixe o seu executor de tarefas tentar de novo ou disparar um alerta. O endpoint retorna 202 quando aceita o evento e 422 se nenhuma automação publicada estiver escutando o gatilho.
Monte a Idempotency-Key a partir de um ID estável, como o do usuário ou o do pedido. Uma chave repetida retorna a execução original em vez de enviar duas vezes, e reutilizar uma chave com um corpo diferente gera 409 IDEMPOTENCY_CONFLICT. O Brew guarda a chave de um disparo pelo mesmo tempo que o registro de recebimento do gatilho, ou seja, 90 dias, e não as 24 horas que muitas APIs usam. Trate as chaves como permanentes, uma por ação. Uma chave baseada em timestamp muda a cada nova tentativa, e uma chave reutilizada semanas depois para outra ação pode devolver a execução anterior em vez de gerar um envio.
O código de produção chama esse endpoint com uma chave de API, não pelo MCP. Via OAuth, o MCP pede que uma pessoa confirme envios reais, disparos de gatilho e publicações: certo para um chat, errado para um handler de requisições. O guia MCP ou REST explica essa divisão.
EMAILS.md.O guia de migração do Loops segue o mesmo padrão.
Os e-mails de confirmação e de link mágico do Supabase continuam saindo do Supabase. A integração do Brew com o Supabase escuta eventos de usuários para iniciar automações, em vez de substituir os e-mails do Supabase Auth. Uma fatura detalhada, montada a partir de dados complexos no momento do envio, também pode ser mais simples de manter no código, então confira antes o que as suas tags de mesclagem precisam. Mantenha cópias importadas no canvas para que esses e-mails continuem visíveis.
Audite o conjunto inteiro. Rode o audit_email em um grupo, corrija e rode de novo antes de envios grandes.
Confira em clientes reais. O test_email_rendering retorna capturas de tela de clientes de e-mail e dispositivos específicos, inclusive no modo escuro, por 10 créditos por execução. O create_inbox_placement_test envia uma cópia real para caixas de teste e informa onde ela chegou, também por 10 créditos.
Deixe o conjunto consistente. Corrija as inconsistências no editor ou peça ao agente que aplique uma mudança, como um rodapé novo, a um grupo inteiro com edit_email.
Leia os resultados. O get_email_analytics informa envios e engajamento. O list_insights retorna os insights que o Brew detectou na sua conta, cada um com as métricas que o embasam.
Teste variantes de forma deliberada. Crie variantes no canvas e direcione os contatos com uma etapa Divisão baseada em comportamento, por exemplo se eles clicaram. O Brew não escolhe para você o vencedor de uma divisão aleatória, então compare as variantes você mesmo.
Mantenha os novos e-mails visíveis. Acrescente uma linha ao AGENTS.md ou a um arquivo parecido: novos e-mails do produto são criados no Brew, disparados por gatilho e adicionados ao EMAILS.md no mesmo pull request.
Sim. O import_email aceita HTML renderizado, MJML, uma mensagem .eml salva ou um módulo do React Email. O HTML renderizado é o mais confiável, porque o módulo é lido de forma estática: as props viram os valores padrão ou de prévia, e componentes importados de outros arquivos locais ficam de fora.
Não. Não há integração, importação nem exportação com o Resend. Você importa o HTML que o seu código renderiza e, se migrar, o seu código dispara um gatilho do Brew em vez de chamar o Resend.
Não. Use o MCP a partir de um agente de programação para configuração, importações, auditorias e testes. O código de produção deve chamar a API REST com uma chave de API, porque, nas conexões OAuth, o MCP pede que uma pessoa confirme envios reais e disparos de gatilho.
A importação converte o e-mail em um design editável do Brew e mantém o HTML original para o envio. Ela remove scripts, iframes, formulários e manipuladores de eventos, neutraliza os pixels de rastreamento que já existiam e hospeda de novo as imagens públicas. Revise cada e-mail no canvas, nas visualizações de desktop e de celular, antes de enviá-lo pelo Brew.
