Si tus emails viven en el código: haz un inventario con un agente de programación, reúnelos en un lienzo de Brew por MCP y mígralos y mejóralos sin riesgos.

Los emails en el código son emails de producto definidos en tu base de código y que se lanzan como cualquier otra funcionalidad. En un equipo que trabaja con Resend y React Email, pregunta qué emails envía el producto y verás cómo se hace el silencio. El email de bienvenida es un componente en emails/. El de restablecimiento de contraseña vive en el panel del proveedor de autenticación. El de fin de la prueba es una cadena de texto en una tarea cron. El recibo lo envía Stripe. Y nadie recuerda qué envía el email de “te echamos de menos”.
Cada email fue fácil de lanzar, así que nadie tiene la visión de conjunto. Nosotros lo arreglaríamos por orden: primero la visibilidad, después la migración y, por último, la mejora. El agente de programación que ya usas puede hacer casi todo, conectado a Brew por MCP.
import_email. La importación es determinista, no usa IA y es gratuita.React Email y Resend mejoraron muchísimo el email para los desarrolladores: componentes tipados y revisados en pull requests. El costo aparece más tarde.
Los emails están dispersos. Componentes, HTML en línea dentro de una tarea en segundo plano, plantillas en el panel de un tercero, emails que otro servicio envía por ti. Ningún archivo los recoge todos.
Las vistas previas no son producción. El servidor de vistas previas de React Email muestra una plantilla con sus props de vista previa, no lo que recibió un cliente, y nada de las demás plantillas.
Solo el equipo de ingeniería puede verlos. Producto, diseño y soporte tendrían que pedirle a alguien de ingeniería que ejecute el proyecto, así que casi nunca lo hacen.
Nada se revisa después del lanzamiento. El email de hace dos años sigue con el logo antiguo y un plan que ya no vendes. Nadie lo nota porque nadie los ve juntos.
El objetivo es una vista compartida de todos los emails. Nada de esto toca producción.

Cada fase es útil por sí sola, y puedes detenerte después de cualquiera de ellas.
El servidor MCP de Brew está en https://brew.new/api/mcp y usa OAuth: inicias sesión con Brew y eliges la marca en la que puede trabajar el agente. En Claude Code:
claude mcp add --transport http brew "https://brew.new/api/mcp"El comando solo guarda el servidor en la configuración local de este proyecto. Para usarlo en todos los proyectos, añade --scope user. Ejecuta claude mcp login brew, o abre /mcp y autentica la conexión de Brew, antes de usar sus herramientas.
La documentación para configurar clientes incluye apartados para Cursor y Codex. Los clientes sin OAuth pueden usar una clave de API limitada a la marca. Después, pide al agente que llame a get_brew_capabilities para que lea la lista actual de herramientas en lugar de adivinarla.
Pide al agente una lista:
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.Conserva la sección “Unknown” (lo que no se pudo identificar). Las confirmaciones de autenticación y los recibos de Stripe no son llamadas de envío de tu código, pero forman parte de la lista. Haz commit de EMAILS.md. Si te detienes aquí, ya tienes una lista que ayer no tenías.
El mejor material para importar es el HTML que tu código ya produce. La utilidad render de React Email te da esa cadena:
import { render } from 'react-email'
import WelcomeEmail from './emails/welcome'
const html = await render(
<WelcomeEmail firstName="Ana" planName="Pro" trialDays={14} />
)Los proyectos más antiguos importan render desde @react-email/render. Usa datos de prueba realistas, no "test", porque tu equipo va a revisar estos emails. Pide al agente un script que renderice cada plantilla en un archivo.
Brew también importa directamente un módulo de React Email con format: "jsx": lo lee de forma estática, sin ejecutarlo. Las props toman sus valores predeterminados o los de PreviewProps, los componentes importados desde otros archivos locales se descartan o se rechazan, y await y otras construcciones similares no se aceptan. Si no se trata de un único archivo autónomo, usa el HTML renderizado.
Después, importa cada email renderizado con import_email, con un groupName por área del producto:
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.Los desarrolladores preguntan, con razón, si la importación reescribe sus emails. No lo hace:
assetReport enumera las que no pudo descargar.baseUrl solo resuelve las rutas relativas de las imágenes.Cada grupo tiene su lienzo. Abre brew.new/emails/onboarding y verás toda la secuencia lado a lado, con un selector entre la vista de escritorio y la vista móvil, y comentarios en cada diseño. Los enlaces al lienzo funcionan para quienes han iniciado sesión en tu espacio de trabajo de Brew. No hay un enlace público para compartir, así que invita a las personas que vayan a revisar.
Aquí es donde salen los problemas: dos logos, un botón con un enlace roto, tres pies de página. Ejecuta audit_email antes de corregir nada. Revisa enlaces, imágenes, cumplimiento normativo, tamaño total cargado, compatibilidad con los clientes de correo, accesibilidad y textos; cuesta 5 créditos por auditoría completa y acepta un emailId guardado o HTML sin procesar, así que puedes auditar emails que no has importado. Nuestra checklist de control de calidad (QA) previa al envío explica cada comprobación y cada umbral.
Ya tienes un inventario, todos los emails en un lienzo y una lista de problemas, y tu código sigue enviándolo todo como antes.
Ahora Brew envía algunos emails en lugar de tu código. No hace falta trasladar todos los emails, y cada tipo de email lo envía un único sistema en cada momento.
| En tu código actual | En Brew |
|---|---|
resend.emails.send({ react: <WelcomeEmail /> }) |
Un evento disparador más una automatización publicada con un paso Enviar email |
| Las props que se pasan al componente | Campos del payload del disparador, que se usan como etiquetas de combinación, como {{ firstName | there }} o {{ trigger.planName }} |
El dominio del from |
Un dominio de envío verificado en Brew |
| Tu tabla de usuarios | Contactos, creados a través de la API REST o de una integración |
| Una tarea cron que envía una secuencia | Una automatización con pasos Espera, Filtro y Enviar email |
El agente llama a create_domain para obtener los registros DNS y, cuando ya estén publicados, a verify_domain. Los dominios creados por MCP y por la API son dominios de marketing. Para los restablecimientos de contraseña y los recibos, configura en la aplicación de Brew un dominio con propósito transaccional: omite el pie de página de baja y entrega también a los contactos dados de baja. La documentación sobre cómo verificar tu dominio de envío explica los registros.
MCP actualiza contactos, pero no los crea. Créalos desde tu backend con POST /v1/contacts, que hace un upsert de un contacto o de un lote de hasta 1000 y crea automáticamente los campos personalizados, o sube un CSV en la aplicación. Activar un disparador también crea o actualiza el contacto que va en su payload. Importa solo a personas que aceptaron recibir tus emails. La documentación sobre cómo añadir contactos explica qué espera Brew.
Es fácil pasarlo por alto: el email de bienvenida importado dice literalmente “Hi Ana”. Sustituye cada valor de prueba por una etiqueta de combinación como {{ firstName | there }}, donde el texto que sigue a la barra es el valor predeterminado. Usa el editor de Brew o pide al agente que ejecute edit_email limitado a esas sustituciones. Es una edición con IA que consume créditos, así que revisa la vista previa. La documentación sobre etiquetas de combinación recoge la sintaxis.
El agente crea un disparador con save_trigger. El esquema del payload debe incluir el email del destinatario. Dale un contrato tipado (infer_payload_contract prepara uno a partir de un payload de ejemplo y set_trigger_contract lo guarda) y después obtenlo con get_trigger_contract en formato ts o zod, para que tu backend y Brew coincidan en la forma del payload.
Después, save_automation crea el disparador más un paso Enviar email con el email, el dominio y el asunto. Se queda como borrador hasta que la publicas. dryRun valida el grafo, test_automation envía una copia real a tu dirección y check_trigger confirma que hay una automatización publicada escuchando antes de que producción active nada.
Es el único cambio en el código de tu aplicación. Antes, con 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} />,
})Después, con 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()}`)
}fetch se resuelve incluso cuando la solicitud se rechaza, así que comprueba res.ok y deja que tu sistema de tareas reintente o avise. El endpoint devuelve 202 cuando acepta el evento y 422 si ninguna automatización publicada escucha el disparador.
Construye la Idempotency-Key a partir de un ID estable, como el del usuario o el del pedido. Una clave repetida devuelve la ejecución original en lugar de enviar dos veces, y reutilizar una clave con un cuerpo distinto da 409 IDEMPOTENCY_CONFLICT. Brew conserva la clave de cada activación tanto tiempo como el registro de recepción del disparador, que son 90 días, no las 24 horas que usan muchas API. Trata las claves como permanentes, una por acción. Una clave basada en una marca de tiempo cambia en cada reintento, y una clave reutilizada para otra acción semanas después puede tratarse como una repetición y no llegar a enviarse.
El código de producción llama a este endpoint con una clave de API, no por MCP. Con OAuth, MCP pide a una persona que confirme los envíos reales, las activaciones de disparadores y las publicaciones: está bien para un chat, pero no para un controlador de solicitudes. La guía de MCP o REST explica el reparto.
EMAILS.md.La guía de migración desde Loops sigue el mismo patrón.
Los emails de confirmación y de magic link de Supabase siguen saliendo de Supabase. La integración de Brew con Supabase escucha los eventos de usuario para iniciar automatizaciones; no sustituye los emails de Supabase Auth. Una factura detallada que se construye con datos complejos en el momento del envío también puede ser más sencilla en el código, así que comprueba primero qué necesitarían tus etiquetas de combinación. Deja copias importadas en el lienzo para que estos emails sigan a la vista.
Audita el conjunto completo. Ejecuta audit_email en todo un grupo, corrige y vuelve a ejecutarlo antes de los envíos grandes.
Comprueba los clientes reales. test_email_rendering devuelve capturas de clientes de correo y dispositivos concretos, también en modo oscuro, por 10 créditos por ejecución. create_inbox_placement_test envía una copia real a bandejas de entrada semilla e informa de dónde llegó, también por 10 créditos.
Haz que el conjunto sea coherente. Corrige las incoherencias en el editor o pide al agente que aplique un cambio, como un pie de página nuevo, a todo un grupo con edit_email.
Lee los resultados. get_email_analytics informa de los envíos y la interacción. list_insights devuelve los hallazgos que Brew ha detectado en tu cuenta, cada uno con las métricas que lo respaldan.
Prueba variantes de forma deliberada. Crea variantes en el lienzo y reparte los contactos con un paso División según su comportamiento, por ejemplo, si hicieron clic. Brew no elige por ti la variante ganadora de una división aleatoria, así que compáralas por tu cuenta.
Mantén a la vista los emails nuevos. Añade una línea a AGENTS.md o a un archivo similar: los nuevos emails de producto se crean en Brew, se activan mediante un disparador y se añaden a EMAILS.md en el mismo pull request.
Sí. import_email acepta HTML renderizado, MJML, un mensaje .eml guardado o un módulo de React Email. El HTML renderizado es lo más fiable, porque el módulo se lee de forma estática: las props toman sus valores predeterminados o de vista previa, y los componentes importados desde otros archivos locales se quedan fuera.
No. No hay integración, importación ni exportación con Resend. Importas el HTML que renderiza tu código y, si migras, tu código activa un disparador de Brew en lugar de llamar a Resend.
No. Usa MCP desde un agente de programación para la configuración, las importaciones, las auditorías y las pruebas. El código de producción debe llamar a la API REST con una clave de API, porque en las conexiones OAuth MCP pide a una persona que confirme los envíos reales y las activaciones de disparadores.
La importación convierte el email en un diseño de Brew editable y conserva el HTML de origen para el envío. Elimina scripts, iframes, formularios y controladores de eventos, desactiva los píxeles de seguimiento que ya hubiera y vuelve a alojar las imágenes públicas. Revisa cada email en el lienzo, en la vista de escritorio y en la vista móvil, antes de enviarlo desde Brew.
