¿Qué hace AgentMail — Correo Electrónico para Agentes de IA?
AgentMail proporciona a los agentes de IA direcciones de correo electrónico reales (@theagentmail.net) con una API REST. Los agentes pueden enviar y recibir correos electrónicos, registrarse en servicios (GitHub, AWS, Slack, etc.) y obtener códigos de verificación. Un sistema de karma previene el spam y mantiene alta la reputación del dominio compartido.
URL Base: https://api.theagentmail.net
Cuándo Usarlo
- Un agente de IA necesita una bandeja de entrada/salida real para registros, flujos de verificación o comunicación transaccional.
- Necesitas aprovisionar cuentas de AgentMail, enviar mensajes, leer el contenido de la bandeja de entrada o registrar webhooks de entrada.
- Necesitas monitorizar el uso del karma o conectar eventos de correo electrónico en la automatización del agente.
Inicio Rápido
Todas las solicitudes requieren el encabezado Authorization: Bearer am_... (clave API del panel de control).
Crear una cuenta de correo electrónico (-10 karma)
curl -X POST https://api.theagentmail.net/v1/accounts \
-H "Authorization: Bearer am_..." \
-H "Content-Type: application/json" \
-d '{"address": "[email protected]"}'
Respuesta: {"data": {"id": "...", "address": "[email protected]", "displayName": null, "createdAt": 123}}
Enviar correo electrónico (-1 karma)
curl -X POST https://api.theagentmail.net/v1/accounts/{accountId}/messages \
-H "Authorization: Bearer am_..." \
-H "Content-Type: application/json" \
-d '{
"to": ["[email protected]"],
"subject": "Hola desde mi agente",
"text": "Cuerpo de texto plano",
"html": "<p>Cuerpo HTML opcional</p>"
}'
Campos opcionales: cc, bcc (arrays de cadenas), inReplyTo, references (cadenas para enhebrado/hilos), attachments (array de {filename, contentType, content} donde content está en base64).
Leer bandeja de entrada
# Listar mensajes
curl https://api.theagentmail.net/v1/accounts/{accountId}/messages \
-H "Authorization: Bearer am_..."
# Obtener mensaje completo (con cuerpo y adjuntos)
curl https://api.theagentmail.net/v1/accounts/{accountId}/messages/{messageId} \
-H "Authorization: Bearer am_..."
Consultar karma
curl https://api.theagentmail.net/v1/karma \
-H "Authorization: Bearer am_..."
Respuesta: {"data": {"balance": 90, "events": [...]}}
Registrar webhook (entrada en tiempo real)
curl -X POST https://api.theagentmail.net/v1/accounts/{accountId}/webhooks \
-H "Authorization: Bearer am_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://my-agent.example.com/inbox"}'
Las entregas de webhook incluyen dos encabezados de seguridad:
X-AgentMail-Signature-- Digest HMAC-SHA256 en hexadecimal del cuerpo de la solicitud, firmado con el secreto del webhookX-AgentMail-Timestamp-- Marca de tiempo en milisegundos de cuándo se envió la entrega
Verifica la firma y rechaza solicitudes con marcas de tiempo de más de 5 minutos para evitar ataques de repetición:
import { createHmac } from "crypto";
const verifyWebhook = (body: string, signature: string, timestamp: string, secret: string) => {
if (Date.now() - Number(timestamp) > 5 * 60 * 1000) return false;
return createHmac("sha256", secret).update(body).digest("hex") === signature;
};
Descargar adjunto
curl https://api.theagentmail.net/v1/accounts/{accountId}/messages/{messageId}/attachments/{attachmentId} \
-H "Authorization: Bearer am_..."
Devuelve {"data": {"url": "https://signed-download-url..."}}.
Referencia completa de la API
| Método | Ruta | Descripción | Karma |
|---|---|---|---|
| POST | /v1/accounts | Crear cuenta de correo | -10 |
| GET | /v1/accounts | Listar todas las cuentas | |
| GET | /v1/accounts/:id | Obtener detalles de la cuenta | |
| DELETE | /v1/accounts/:id | Eliminar cuenta | +10 |
| POST | /v1/accounts/:id/messages | Enviar correo electrónico | -1 |
| GET | /v1/accounts/:id/messages | Listar mensajes | |
| GET | /v1/accounts/:id/messages/:msgId | Obtener mensaje completo | |
| GET | /v1/accounts/:id/messages/:msgId/attachments/:attId | Obtener URL del adjunto | |
| POST | /v1/accounts/:id/webhooks | Registrar webhook | |
| GET | /v1/accounts/:id/webhooks | Listar webhooks | |
| DELETE | /v1/accounts/:id/webhooks/:whId | Eliminar webhook | |
| GET | /v1/karma | Obtener saldo + eventos |
Sistema de karma
Cada acción tiene un costo o recompensa de karma:
| Evento | Karma | Por qué |
|---|---|---|
money_paid | +100 | Comprar créditos |
email_received | +2 | Alguien respondió desde un dominio de confianza |
account_deleted | +10 | Karma reembolsado al eliminar una dirección |
email_sent | -1 | Enviar cuesta karma |
account_created | -10 | Crear direcciones cuesta karma |
Reglas importantes:
- El karma solo se otorga por correos entrantes de proveedores de confianza (Gmail, Outlook, Yahoo, iCloud, ProtonMail, Fastmail, Hey, etc.). Los correos de dominios desconocidos/descartables no generan karma.
- Solo se gana karma una vez por remitente hasta que el agente responda. Si el remitente X te envía 5 correos sin respuesta, solo el primero genera karma. Responde a X, y el siguiente correo de X volverá a generar karma.
- Eliminar una cuenta reembolsa los 10 de karma que costó crearla.
Cuando el karma llega a 0, los envíos y la creación de cuentas devuelven HTTP 402. Siempre verifica el saldo antes de operaciones que cuesten karma.
SDK de TypeScript
import { createClient } from "@agentmail/sdk";
const mail = createClient({ apiKey: "am_..." });
// Crear cuenta
const account = await mail.accounts.create({
address: "[email protected]",
});
// Enviar correo
await mail.messages.send(account.id, {
to: ["[email protected]"],
subject: "Hola",
text: "Enviado por un agente de IA.",
});
// Leer bandeja de entrada
const messages = await mail.messages.list(account.id);
const detail = await mail.messages.get(account.id, messages[0].id);
// Adjuntos
const att = await mail.attachments.getUrl(accountId, messageId, attachmentId);
// att.url es una URL de descarga firmada
// Webhooks
await mail.webhooks.create(account.id, {
url: "https://my-agent.example.com/inbox",
});
// Karma
const karma = await mail.karma.getBalance();
console.log(karma.balance);
Manejo de errores
import { AgentMailError } from "@agentmail/sdk";
try {
await mail.messages.send(accountId, { to: ["[email protected]"], subject: "Hola", text: "Hey" });
} catch (e) {
if (e instanceof AgentMailError) {
console.log(e.status); // 402, 404, 401, etc.
console.log(e.code); // "INSUFFICIENT_KARMA", "NOT_FOUND", etc.
console.log(e.message);
}
}
Patrones comunes
Registrarse en un servicio y leer el correo de verificación
const account = await mail.accounts.create({
address: "[email protected]",
});
// Usa la dirección para registrarte (automatización del navegador, API, etc.)
// Sondea en busca del correo de verificación
for (let i = 0; i < 30; i++) {
const messages = await mail.messages.list(account.id);
const verification = messages.find(m =>
m.subject.toLowerCase().includes("verify") ||
m.subject.toLowerCase().includes("confirm")
);
if (verification) {
const detail = await mail.messages.get(account.id, verification.id);
// Analiza el enlace/código de verificación de detail.bodyText o detail.bodyHtml
break;
}
await new Promise(r => setTimeout(r, 2000));
}
Enviar un correo y esperar respuesta
const sent = await mail.messages.send(account.id, {
to: ["[email protected]"],
subject: "Pregunta sobre el pedido #12345",
text: "¿Puedes comprobar el estado?",
});
for (let i = 0; i < 60; i++) {
const messages = await mail.messages.list(account.id);
const reply = messages.find(m =>
m.direction === "inbound" && m.timestamp > sent.timestamp
);
if (reply) {
const detail = await mail.messages.get(account.id, reply.id);
// Procesa la respuesta
break;
}
await new Promise(r => setTimeout(r, 5000));
}
Tipos
type Account = { id: string; address: string; displayName: string | null; createdAt: number };
type Message = { id: string; from: string; to: string[]; subject: string; direction: "inbound" | "outbound"; status: string; timestamp: number };
type MessageDetail = Message & { cc: string[] | null; bcc: string[] | null; bodyText: string | null; bodyHtml: string | null; inReplyTo: string | null; references: string | null; attachments: AttachmentMeta[] };
type AttachmentMeta = { id: string; filename: string; contentType: string; size: number };
type KarmaBalance = { balance: number; events: KarmaEvent[] };
type KarmaEvent = { id: string; type: string; amount: number; timestamp: number; metadata?: Record<string, unknown> };
Limitaciones
- Usa esta habilidad solo cuando la tarea coincida claramente con el alcance descrito anteriormente.
- No trates la salida como un sustituto de la validación específica del entorno, las pruebas o la revisión por expertos.
- Detente y pide aclaraciones si faltan las entradas requeridas, los permisos, los límites de seguridad o los criterios de éxito.