SDK de MailMask

Skills para tu agente, cliente oficial TypeScript/JavaScript, API REST, SMTP y servidor MCP. Crea aliases, gestiona dominios, envía emails y más — desde tu código o pidiéndoselo a tu agente.

npm: @easybits.cloud/mailmask

Recomendado

Usa MailMask con tu agente

No hace falta leer esta página: instálale a tu agente (Claude Code, Cursor, Codex, Copilot…) las skills oficiales y pídele lo que necesitas en español — “crea hola@ que reenvíe a mi Gmail”, “apunta el dominio a Vercel”, “avísale a mi app cuando llegue correo”. Las skills saben qué puede hacer cada plan, qué secretos salen una sola vez y qué no se debe borrar.

1. Instala las skills

npx skills add https://www.mailmask.studio

2. Dale una API key a tu agente

3. Conecta el servidor MCP con esa llave y el agente opera la cuenta por ti. Cinco skills, instala todas o solo la que necesitas:

  • mailmask-account: opera la cuenta entera en tu nombre (dominios, máscaras, buzones, reglas, webhooks, envío) con sus reglas de seguridad.
  • mailmask-mcp: conecta el cliente MCP y sabe leer sus resultados (403 con precio, 409 con suggestedValues, secretos que salen una sola vez).
  • mailmask-sdk: usa @easybits.cloud/mailmask desde código, incluida la verificación de firma de webhooks.
  • mailmask-dns: apunta el dominio a Vercel, Netlify, GitHub Pages… sin romper el correo.
  • mailmask-docs: lee esta documentación, llms.txt y el OpenAPI sin scrapear.

Formato Agent Skills. Espejo en GitHub: npx skills add blissito/mailmask-skills. Índice en /.well-known/agent-skills/index.json; cada skill en /skills/<nombre>/SKILL.md.

Instalación

npm install @easybits.cloud/mailmask

Autenticación

Para usar el SDK necesitas una API Key. Puedes crear una desde el Dashboard en la sección de API Keys. Disponible en todas las cuentas, gratis incluidas.

Las API keys tienen el prefijo mk_ y solo se muestran una vez al crearlas. Guárdala en un lugar seguro.

import { MailMask } from "@easybits.cloud/mailmask";

const mm = new MailMask({ apiKey: "mk_..." });

Dominios

Gestiona los dominios conectados a tu cuenta. Puedes listar, agregar, verificar y consultar el estado de salud DNS.

// Listar dominios
const domains = await mm.domains.list();

// Obtener un dominio
const domain = await mm.domains.get("domain-id");

// Agregar dominio: devuelve los registros DNS que hay que publicar
const { domain, dnsRecords, requiereActivacion } = await mm.domains.create("example.com");
dnsRecords.mx;           // { type: "MX", name, value: "10 inbound-smtp.us-east-1.amazonaws.com", priority: 10 }
dnsRecords.verification; // { type: "TXT", name: "_amazonses.example.com", value }
dnsRecords.dkim;         // 3 CNAME
dnsRecords.spf;          // { type: "TXT", name, value: "v=spf1 include:amazonses.com ~all" }
// requiereActivacion: true si es tu 2.º dominio: se guarda pero no reenvía hasta activarlo ($99/mes)

// Eliminar dominio
await mm.domains.delete("domain-id");

// Verificar DNS
await mm.domains.verify("domain-id");

// Estado de salud
const health = await mm.domains.health("domain-id");

Aliases

Crea y administra aliases (máscaras) de email. Cada alias reenvía correo a uno o múltiples destinos, guarda el correo en un buzón IMAP propio, o las dos cosas. El dominio gratis permite 5 aliases; el activado, ilimitados. * como alias es el catch-all.

// Listar aliases
const aliases = await mm.aliases.list("domain-id");

// Crear alias
const alias = await mm.aliases.create("domain-id", {
  alias: "hello",
  destinations: ["me@gmail.com"]
});

// Crear alias con múltiples destinos
const alias = await mm.aliases.create("domain-id", {
  alias: "team",
  destinations: ["alice@company.com", "bob@company.com"]
});

// Actualizar alias
await mm.aliases.update("domain-id", "hello", { enabled: false });

// Eliminar alias
await mm.aliases.delete("domain-id", "hello");

Buzones IMAP

En un dominio activado, un alias puede tener un buzón real para leer y enviar desde Apple Mail, Outlook o Thunderbird (IMAP 993 y SMTP 465 con TLS). Buzones ilimitados con 10 GB compartidos por dominio, ampliables en bloques de +50 GB. La contraseña se devuelve una sola vez y MailMask no la guarda; si se pierde, se regenera desde el panel. Un alias con buzón puede ir sin destinos de reenvío.

// Alias con buzón (y sin reenvío)
const ventas = await mm.aliases.create("domain-id", { alias: "ventas", mailbox: true });
ventas.buzon;      // { email, password, quotaBytes, imap: { host, port: 993 }, smtp: { host, port: 465 } }
ventas.errorBuzon; // texto si el alias se creó pero el buzón no

// Buzón para un alias que ya existe
const buzon = await mm.aliases.createMailbox("domain-id", "hello");

// Borrar el buzón Y SU CORREO (el alias conserva sus destinos)
// Contraseña nueva (se devuelve una sola vez)
const { password } = await mm.aliases.resetMailboxPassword("domain-id", "hello");
await mm.aliases.deleteMailbox("domain-id", "hello");

DNS

El editor de DNS viene con el dominio activado, y crear o editar registros es cosa del dueño o un administrador: un agente de la Bandeja puede verlos, no cambiarlos. Si MailMask lleva el DNS de tu dominio, puedes editar sus registros desde el SDK. Para un dominio que registraste fuera, primero se crea la zona: copiamos lo que encontremos de tu proveedor actual y te damos los nameservers que hay que cambiar en tu registrador. Hasta que los cambies, nada de lo que edites tiene efecto.

// Estado de la zona y registros actuales
const { zone, records } = await mm.dns.list("domain-id");
console.log(zone.status); // "none" | "pending_delegation" | "active"

// Crear la zona (importa tu DNS actual y devuelve los nameservers)
const z = await mm.dns.createZone("domain-id");
console.log(z.nameservers, z.imported);

// ¿Ya apunta a nosotros?
const d = await mm.dns.delegation("domain-id");
console.log(d.delegated, d.observed);

upsert reemplaza el conjunto de valores de ese nombre y tipo. Para añadir un valor, lee primero con list e incluye también los que ya estaban.

// Crear o reemplazar un registro
await mm.dns.upsert("domain-id", {
  name: "www",        // "@" para la raíz
  type: "CNAME",
  values: ["cname.vercel-dns.com"],
  ttl: 300,
});

// En MX la prioridad va dentro del valor
await mm.dns.upsert("domain-id", { name: "sub", type: "MX", values: ["10 mail.ejemplo.com"] });

// Borrar un registro completo
await mm.dns.delete("domain-id", "www", "CNAME");

// Apuntar el dominio a un hosting sin saber qué registros hacen falta
await mm.dns.preset("domain-id", "vercel", "mi-proyecto.vercel.app");
// vercel | netlify | github-pages | cloudflare-pages | render | fly | redirect-a-www | dmarc

Los registros de tu correo están protegidos. El MX, el TXT de verificación y los CNAME de DKIM vienen marcados con managed: true y no se pueden borrar: si los quitas dejas de recibir correo. El TXT de la raíz sí se puede editar mientras conserve el SPF de MailMask; si lo omites, la respuesta trae suggestedValues con los valores ya fusionados.

Reglas

Las reglas permiten enrutar o filtrar emails automáticamente según el destinatario, el remitente o el asunto. Vienen con el dominio activado (el dominio gratis no incluye reglas).

// Listar reglas
const rules = await mm.rules.list("domain-id");

// Crear regla
const rule = await mm.rules.create("domain-id", {
  field: "from",
  match: "contains",
  value: "@newsletter.com",
  action: "forward",
  target: "newsletters@gmail.com"
});

// Actualizar regla
await mm.rules.update("domain-id", "rule-id", { enabled: false });

// Eliminar regla
await mm.rules.delete("domain-id", "rule-id");

Envío de emails

Envía emails individuales o masivos desde cualquier alias activo de tu dominio. El cuerpo puede ir como html, body (texto plano) o markdown; replyTo es opcional.

La firma del dominio —y su logo— sólo se añade cuando mandas markdown. Con html o body el correo sale tal cual lo escribiste, sin firma: así un correo transaccional que ya trae su propio pie no termina con dos firmas.

// Enviar email
await mm.send.send("domain-id", {
  from: "hello",
  fromName: "Mi Tienda",
  to: "client@example.com",
  subject: "Hola!",
  html: "<p>Contenido del email</p>"
});

// Envío masivo
const job = await mm.send.bulkSend("domain-id", {
  from: "hello",
  recipients: ["a@test.com", "b@test.com"],
  subject: "Newsletter",
  html: "<p>Contenido</p>"
});

// Consultar estado del envío masivo
const status = await mm.send.bulkStatus("domain-id", job.jobId);
console.log(status.sent, status.failed, status.skippedSuppressed); // skipped: en lista de supresión

Copias, hilo y adjuntos

cc y bcc aceptan hasta 20 direcciones y pasan por la lista de supresión. Para que la respuesta caiga en el hilo del cliente, manda inReplyTo y references con el Message-ID original. Los adjuntos se suben primero (máx. 5 MB, sin ejecutables) y la llave se pasa al enviar; el archivo se borra de S3 en cuanto sale el correo.

// 1. Subir el archivo
const pdf = await mm.attachments.upload("domain-id", {
  filename: "factura-0042.pdf",
  contentType: "application/pdf",
  data: await fs.readFile("./factura.pdf"), // Uint8Array o Blob
});

// 2. Enviar con copias, hilo y adjunto
await mm.send.send("domain-id", {
  from: "facturacion",
  to: "cliente@example.com",
  cc: ["contador@example.com"],
  bcc: ["archivo@midominio.com"],
  subject: "Re: Factura 0042",
  markdown: "Adjunto la **factura 0042**.",
  inReplyTo: "<abc123@mail.example.com>",
  references: "<abc123@mail.example.com>",
  attachments: [pdf],
});

Idempotencia

Si tu proceso reintenta, pasa una clave de idempotencia (máx. 128 caracteres). Durante 24 horas, la misma clave devuelve la respuesta original sin volver a enviar ni consumir cuota. La respuesta repetida lleva el header Idempotent-Replayed: true.

await mm.send.send("domain-id", { to, subject, html }, {
  idempotencyKey: `pedido-${pedido.id}`,
});

Logs

Consulta el historial de emails procesados por un dominio. Los reenvíos entrantes llevan forwarded, discarded, failed o rule_matched. Los envíos que haces por API nacen en sent y, cuando SES avisa, pasan a delivered, bounced o complained; el motivo del rebote va en error.

// Últimos 50 por omisión
const logs = await mm.logs.list("domain-id");

// Hasta 100 (máximo)
const more = await mm.logs.list("domain-id", { limit: 100 });

Lista de supresión

Cuando un destinatario rebota de forma permanente o marca tu correo como spam, SES lo avisa y MailMask lo agrega a la lista de supresión del dominio. Desde entonces send, bulkSend y el reenvío lo saltan: insistirle daña la reputación de tu dominio. Un envío a una dirección suprimida responde 422.

// Ver quién está bloqueado y por qué
const lista = await mm.suppressions.list("domain-id");
// [{ email, reason: "bounce:Permanent" | "complaint" | "manual", createdAt }]

// Bloquear a mano (p. ej. alguien que pidió no recibir más)
await mm.suppressions.add("domain-id", "persona@example.com");

// Liberar tras corregir la dirección
await mm.suppressions.remove("domain-id", "persona@example.com");

Webhooks

Recibe un POST en tu servidor cada vez que pasa algo en el dominio. Disponible en dominio activado. La URL debe ser https y pública; máximo 10 webhooks por dominio.

// Crear. El secret se muestra una sola vez: guárdalo.
const wh = await mm.webhooks.create("domain-id", {
  url: "https://miapp.com/webhooks/mailmask",
  events: ["email.received", "email.bounced"],
});
console.log(wh.secret); // whsec_...

// Probar: encola un evento `ping` que llega en el siguiente minuto
await mm.webhooks.test("domain-id", wh.id);

// Ver las últimas 50 entregas, con intentos y código HTTP
const entregas = await mm.webhooks.deliveries("domain-id", wh.id);

// Pausar, editar, borrar
await mm.webhooks.update("domain-id", wh.id, { enabled: false });
await mm.webhooks.delete("domain-id", wh.id);

Verificar la firma

Cada entrega lleva X-MailMask-Event, X-MailMask-Delivery, X-MailMask-Timestamp (ms Unix) y X-MailMask-Signature = sha256=HMAC(secret, timestamp + "." + cuerpo). Verifica con el cuerpo crudo, antes de parsear el JSON. El SDK trae la función; corre en Node, Deno, Bun y Workers.

import { verifyWebhookSignature } from "@easybits.cloud/mailmask";

app.post("/webhooks/mailmask", async (req, res) => {
  const raw = req.body.toString(); // cuerpo sin parsear
  const ok = await verifyWebhookSignature(process.env.MAILMASK_WEBHOOK_SECRET, {
    signature: req.headers["x-mailmask-signature"],
    timestamp: req.headers["x-mailmask-timestamp"],
  }, raw);
  if (!ok) return res.status(401).end();

  const { event, data } = JSON.parse(raw);
  if (event === "email.bounced") await marcarCorreoInvalido(data.recipient);
  res.status(200).end(); // responde 2xx rápido; procesa después
});

Reintentos: si tu servidor no responde 2xx en 10 segundos, MailMask reintenta a 1 min, 5 min, 30 min, 2 h y 12 h; después marca la entrega como failed. Las entregas se conservan 7 días.

Credenciales SMTP

Crea credenciales SMTP para enviar emails desde tu código o aplicaciones SaaS. Disponible en dominio activado.

// Listar credenciales
const creds = await mm.smtp.list("domain-id");

// Crear credencial
const cred = await mm.smtp.create("domain-id", "Mi app");
console.log(cred.server, cred.port, cred.username); // email-smtp.<region>.amazonaws.com, 587, STARTTLS
console.log(cred.password); // Solo se muestra una vez

// Revocar credencial
await mm.smtp.revoke("domain-id", "cred-id");

API Keys

Administra tus API keys programáticamente. Útil para rotación automática de credenciales.

// Listar keys
const keys = await mm.apiKeys.list();

// Crear key
const newKey = await mm.apiKeys.create("Mi nueva key");
console.log(newKey.key); // mk_... — solo se muestra una vez

// Revocar key
await mm.apiKeys.revoke("key-id");

Manejo de errores

El SDK lanza MailMaskError para respuestas HTTP no exitosas. Incluye el código de estado y el mensaje del servidor.

import { MailMask, MailMaskError } from "@easybits.cloud/mailmask";

try {
  await mm.domains.create("example.com");
} catch (err) {
  if (err instanceof MailMaskError) {
    console.error(err.status, err.message);
  }
}

MCP Server

MailMask expone un servidor MCP (Model Context Protocol) en https://www.mailmask.studio/mcp, con transporte Streamable HTTP y sin sesiones. Un agente (Claude Code, Claude Desktop, Cursor, cualquier cliente MCP) puede dar de alta dominios, máscaras, buzones IMAP, reglas y webhooks, y enviar correo, con la misma API key de la sección anterior.

# Claude Code
claude mcp add --transport http mailmask https://www.mailmask.studio/mcp \
  --header "Authorization: Bearer mk_..."
// Claude Desktop, Cursor y otros clientes (mcp.json)
{
  "mcpServers": {
    "mailmask": {
      "type": "http",
      "url": "https://www.mailmask.studio/mcp",
      "headers": { "Authorization": "Bearer mk_..." }
    }
  }
}

Herramientas (cada una es un método del SDK, con las mismas reglas y límites):

Sobre el DNS: set_dns_record reemplaza el conjunto de valores de ese nombre y tipo, así que para añadir un valor hay que leer primero con list_dns_records e incluir también los que ya estaban. Los registros que MailMask necesita para tu correo (el MX, el TXT de verificación, el SPF y los CNAME de DKIM) vienen marcados con managed y no se pueden borrar: el agente recibe un 409 explicando por qué.

Un error del servidor (por ejemplo, un dominio gratis pidiendo un buzón) llega al agente como resultado con isError y el mensaje tal cual, no como fallo del protocolo. Las API keys no se crean ni revocan por MCP: un agente con una llave no puede fabricar más. El límite es el mismo de la API: 60 peticiones por minuto por llave.

Referencia API

Para la referencia completa de la API HTTP (todos los endpoints, parámetros y respuestas), consulta la documentación interactiva:

© MailMask — mailmask.studio