Vanessa LozzardoConstruyendo un asistente de email con IA usando Sendkit
Combina un LLM con la API de Sendkit para construir un asistente que redacta, envía y monitorea email en nombre de tus usuarios.

Tu usuario escribe "envíale un email a John avisándole que movemos el standup a las 3 PM". Treinta segundos después, un mensaje bien redactado aterriza en el buzón de John: redactado por un LLM, aprobado por el usuario, enviado a través de tu infraestructura y monitoreado de punta a punta. Ese es el patrón del asistente de email con IA, y es sorprendentemente directo de construir.
Este tutorial recorre la arquitectura, el código y las aristas que necesitas limar antes de llevar esto a producción. Usaremos OpenAI para la capa del LLM y la API de email de Sendkit para enviar y monitorear la entrega.
Qué hace realmente un asistente de email con IA
Quítale el hype y te queda un pipeline con cuatro pasos:
- Parsear la intención: El usuario dice algo como "email a John sobre la reunión". El LLM extrae el destinatario, el tema y el tono.
- Redactar: El LLM genera un asunto y un cuerpo con base en la intención extraída más cualquier contexto disponible (hilos previos, datos de calendario, preferencias del usuario).
- Aprobar: El borrador se muestra al usuario. Puede editar, regenerar o enviar.
- Enviar y monitorear: El email aprobado sale vía la API de Sendkit. Los webhooks reportan la entrega, rebotes y aperturas de vuelta a tu app.
Cada paso importa. Salta el paso de aprobación y eventualmente enviarás algo vergonzoso. Salta el monitoreo de entrega y estás volando a ciegas.

Visión general de la arquitectura
Así se ve el flujo:
User input → LLM (draft) → Preview UI → User approves → Sendkit API (send)
↓
Webhook events (delivered/bounced/opened)
↓
Feed status back to assistant contextEl backend es un servicio Node.js. La llamada al LLM y el envío del email son llamadas separadas a la API: nunca las combines en un solo paso. Quieres una puerta humana entre "la IA escribió algo" y "ese algo se envió a una persona real".
Construyendo el paso de borrador
El paso de borrador llama a tu LLM con la intención del usuario y devuelve salida estructurada. Usa function calling o un esquema JSON para imponer la forma de la respuesta.
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const draftEmail = async (userIntent, recipientContext) => {
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are an email drafting assistant. Given user intent and context, produce a JSON object with "subject" (string) and "body" (string, plain text). Keep the tone professional but natural. Do not invent facts.`,
},
{
role: 'user',
content: `Intent: ${userIntent}\nRecipient context: ${JSON.stringify(recipientContext)}`,
},
],
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
};
// Usage
const draft = await draftEmail('Tell John the standup is moving to 3 PM tomorrow', {
name: 'John Park',
email: '[email protected]',
relationship: 'teammate',
});
console.log(draft.subject); // "Standup time change — moving to 3 PM"
console.log(draft.body); // "Hey John, quick heads up..."Fuerza la salida JSON. Si dejas que el LLM devuelva texto libre, gastarás la mitad de tu tiempo parseándolo y la otra mitad depurando por qué se rompió.
Human-in-the-loop
Esta es la parte que la gente salta cuando está entusiasmada con la IA. No la saltes.
Presenta el borrador al usuario en tu UI. Dale tres opciones: Editar, Regenerar o Enviar. Guarda el borrador del lado del servidor con un ID único para que la acción de enviar referencie un payload aprobado, no lo que sea que el cliente envíe.
import { randomUUID } from 'crypto';
const drafts = new Map();
const saveDraft = (draft, recipientEmail) => {
const id = randomUUID();
drafts.set(id, {
to: recipientEmail,
subject: draft.subject,
body: draft.body,
approved: false,
createdAt: Date.now(),
});
return id;
};
const approveDraft = draftId => {
const draft = drafts.get(draftId);
if (!draft) throw new Error('Draft not found');
draft.approved = true;
return draft;
};El principio clave: el LLM propone, el usuario dispone. Nunca envíes desde un borrador que no haya sido explícitamente aprobado.
Enviando vía Sendkit
Una vez que el usuario aprueba, envía a través de la API de Sendkit. Instala el SDK:
npm install @sendkitdev/sdkLuego envía el borrador aprobado:
import { Sendkit } from '@sendkitdev/sdk';
const sendkit = new Sendkit(process.env.SENDKIT_API_KEY);
const sendApprovedDraft = async draftId => {
const draft = approveDraft(draftId);
const { data, error } = await sendkit.emails.send({
from: '[email protected]',
to: draft.to,
subject: draft.subject,
html: `<p>${draft.body.replace(/\n/g, '</p><p>')}</p>`,
text: draft.body,
});
if (error) {
console.error('Send failed:', error.message);
return { success: false, error: error.message };
}
console.log('Sent:', data.id);
return { success: true, messageId: data.id };
};El SDK devuelve { data, error } en lugar de lanzar excepciones. Revisa error primero. El valor data.id es tu handle para monitorear la entrega más tarde. Incluye siempre html y text: algunos clientes de correo aún prefieren texto plano. Mira nuestra guía completa de email transaccional con Node.js para más patrones.

Monitoreando la entrega con webhooks
Enviar es la mitad del trabajo. Necesitas saber si el email realmente llegó. Configura un endpoint de webhook en tu dashboard de Sendkit para recibir eventos de entrega.
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhooks/sendkit', (req, res) => {
const { type, data } = req.body;
switch (type) {
case 'email.delivered':
console.log(`Delivered: ${data.messageId} to ${data.to}`);
// Update assistant context: "Your email to John was delivered"
break;
case 'email.bounced':
console.log(`Bounced: ${data.messageId} — ${data.bounceReason}`);
// Alert user: "Email to John bounced — check the address"
break;
case 'email.opened':
console.log(`Opened: ${data.messageId}`);
// Optional: "John opened your email"
break;
}
res.sendStatus(200);
});Alimenta estos eventos de vuelta al contexto del asistente. Cuando el usuario pregunte "¿le llegó el email a John?", el asistente puede responder con datos reales de entrega en lugar de adivinar. Para una mirada más profunda al manejo de rebotes, mira cómo manejar rebotes de email.
Features inteligentes que vale la pena añadir
Una vez que el loop central funciona, añade estas capas:
Validación del destinatario: Antes de que el LLM incluso redacte, valida la dirección de email del destinatario usando la API de validación de email de Sendkit. Atrapa errores tipográficos y direcciones desechables temprano. No tiene sentido redactar un email a [email protected].
const { data: validation } = await sendkit.emailValidations.validate({
email: recipientEmail,
});
if (validation.result !== 'deliverable') {
// Ask user to double-check the address
}Detección de tono: Añade un system prompt que clasifique el tono del borrador (formal, casual, urgente) y lo muestre al usuario. "Esto suena formal, ¿quieres que lo haga más casual?". Toque pequeño, gran victoria de usabilidad.
Sugerencias de hora de envío: Si tienes datos de zona horaria del destinatario, sugiere horas óptimas de envío. Un martes a las 10 AM en la zona horaria del destinatario le gana a un sábado a medianoche.
Conciencia del hilo: Pasa los hilos de email previos al contexto del LLM para que las respuestas se mantengan coherentes. El asistente debería saber qué dijo John la última vez, no solo qué quiere decir el usuario ahora.
Consideraciones de seguridad
Un asistente de email con IA con acceso a una API de envío es un arma apuntada a tu propio pie si eres descuidado. Ciérralo bien.
Allowlisting de destinatarios: Nunca dejes que el LLM elija el destinatario. El usuario provee el destinatario, tu código lo resuelve a una dirección de email desde tu base de datos de contactos, y el LLM solo redacta el contenido. Si el usuario dice "email a toda la empresa", esa es una decisión de lógica de negocio, no una decisión del LLM.
Sanitización de contenido: El LLM podría generar contenido tipo HTML o intentos de inyección (la inyección de prompts vía cuerpo de email es una superficie de ataque real). Sanitiza el cuerpo antes de enviar. Quita scripts, limita las etiquetas HTML y escapa cualquier cosa sospechosa.
Rate limiting: Limita los envíos por usuario por hora. Un usuario entusiasta (o un cliente con bugs) no debería poder disparar 10,000 emails a través de tu asistente. Los precios de Sendkit son basados en uso, así que un loop descontrolado le pega rápido a tu billetera.
Aislamiento de API keys: Usa una API key de Sendkit dedicada para tu asistente con permisos de envío restringidos por dominio. No reutilices la key principal de tu aplicación. Si la key del asistente se filtra, el radio de explosión está contenido.
Audit logging: Registra cada envío con el ID del usuario, el ID del borrador, el destinatario y la marca de tiempo. Cuando algo salga mal (y saldrá), necesitas un rastro.
Juntando todo
El patrón de asistente de email con IA son cuatro preocupaciones cosidas juntas: parseo de intención, generación de contenido, aprobación humana y entrega confiable. El LLM maneja las primeras dos. Tu código maneja la puerta de aprobación. Sendkit maneja la última milla.
Empieza con el loop básico de envío: redactar, aprobar, enviar. Añade monitoreo con webhooks una vez que eso funcione. Añade capas de validación y features inteligentes después de que hayas lanzado la primera versión y tengas usuarios reales diciéndote qué es lo que realmente necesitan.
La documentación completa de Sendkit cubre API keys, verificación de dominio, firmas de webhook y rate limits. Si estás construyendo agentes de IA que envían email autónomamente (sin human-in-the-loop), lee nuestra guía sobre agentes de IA que envían email para los guardrails adicionales que eso requiere.
Construye primero la versión simple. Lánzala. Luego hazla inteligente.
Compartir este artículo