Docs / Módulo / Mail
Módulo de Mail
Correo transaccional por evento: registros, confirmaciones de compra, recuperación de contraseña, notificaciones. Un correo por request, con tu propio HTML y variables de personalización.
¿Buscas newsletters o campañas masivas a listas segmentadas? Ese es un producto distinto — ver Marketing.
Introducción
La API de Mail te permite enviar correos transaccionales directamente desde tu backend, sin configurar SMTP ni gestionar infraestructura de entrega. Envías tu HTML/texto y las variables de personalización; Webability se encarga de la cola, reintentos y entregabilidad.
¿Para qué sirve?
- • Correos de bienvenida y confirmación de cuenta
- • Comprobantes de compra y facturas
- • Recuperación de contraseña con enlaces de un solo uso
- • Notificaciones de estado (envíos, pagos, alertas)
Características
- • Envío síncrono o asíncrono (wait_send)
- • Consulta de estatus por correo (GET /status)
- • Variables de personalización {{variable}}
- • Plantillas registradas reutilizables (template)
- • Envío a múltiples destinatarios en un solo request
Quickstart
Un correo, un request.
CLIENTID="tu-client-id"
TOKEN="tu-token-secreto"
TS=$(date +%s)
PATH="/v1/mail/send"
MSG="POST|${PATH}|${TS}|${CLIENTID}"
DIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$TOKEN" | awk '{print $2}')
curl -s -X POST https://api.webability.info${PATH} \
-H "X-WA-Client: $CLIENTID" \
-H "X-WA-Timestamp: $TS" \
-H "X-WA-Digest: $DIG" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "no-reply@tuempresa.com", "name": "Tu Empresa" },
"to": { "email": "cliente@ejemplo.com", "name": "Ana", "vars": { "nombre": "Ana" } },
"subject": "Confirma tu compra",
"html": "<p>Hola {{nombre}}, tu compra fue confirmada.</p>"
}'
{ "status": "ok", "queue_key": 42, "queue_status": "pending", "to": "cliente@ejemplo.com" }
El correo se encola y se envía en segundo plano. Consulta GET /v1/mail/status/{queue_key} para saber si ya se entregó, o usa wait_send para esperar el resultado en el mismo request.
POST /v1/mail/send
Envía un correo a un solo destinatario.
Body JSON
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| from.email | string | ✓ | Correo remitente |
| from.name | string | Nombre del remitente | |
| to.email | string | ✓ | Correo destinatario |
| to.name | string | Nombre del destinatario | |
| to.vars | object | Variables de personalización, con los nombres que quieras — sin prefijos. Cada clave sustituye {{clave}} en subject/html/text, o dentro del content de la plantilla si usas template — mismo {{clave}}, sin diferencia (ver Con plantilla registrada) | |
| template | string | Id de una plantilla ya registrada, activa y de tipo email (ver Con plantilla registrada) — un template de tipo "layout" no se puede mandar directo. Si template viene, subject/html/text se ignoran | |
| lang | string | Código de idioma destino (BCP-47, p. ej. fr, en, de). Ignorado si no usas template (ver Plantillas en varios idiomas) | |
| subject | string | ✓ * | Asunto. * No requerido si usas template |
| html | string | Cuerpo HTML (al menos uno de html/text, salvo que uses template) | |
| text | string | Cuerpo en texto plano | |
| tags | string[] | Etiquetas libres para tus propios reportes | |
| track_opens | bool | Pixel de apertura | |
| track_clicks | bool | Link wrapping para clics | |
| wait_send | bool | Ver wait_send abajo |
Respuesta (200)
{
"status": "ok",
"queue_key": 42,
"queue_status": "pending",
"to": "cliente@ejemplo.com"
}
queue_status viene "pending" salvo que uses wait_send (ver abajo). El envío real ocurre en segundo plano.
Enviar con una plantilla registrada
En vez de mandar subject/html/text en cada request, puedes registrar una plantilla reutilizable desde tu cuenta y referenciarla por su id con el campo template. El servidor arma el correo con esa plantilla — solo mandas to.vars con los valores a inyectar.
La plantilla debe existir y estar activa para tu cuenta (tabla interna templates_template). El servidor valida esto antes de encolar el correo — si el template no existe o no está activo, la respuesta es un error inmediato (3025/3026, ver Errores), no un envío que falla en segundo plano.
curl -s -X POST https://api.webability.info/v1/mail/send \
-H "X-WA-Client: $CLIENTID" -H "X-WA-Timestamp: $TS" -H "X-WA-Digest: $DIG" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "no-reply@tuempresa.com", "name": "Tu Empresa" },
"to": { "email": "cliente@ejemplo.com", "name": "Ana", "vars": { "nombre": "Ana", "codigo": "482913" } },
"template": "recuperacion"
}'
{ "status": "ok", "queue_key": 55, "queue_status": "pending", "to": "cliente@ejemplo.com" }
Si el id de plantilla no existe (o no pertenece a tu cuenta) o no está activa, la respuesta llega de inmediato con status: "error" — no se encola nada:
{ "status": "error", "code": 3025, "message": "Plantilla 'recuperacion' no encontrada para esta cuenta" }
📝 Las plantillas no se crean por API — se agregan y editan directamente desde Consola → Correos → Plantillas. Ahí defines el id que vas a usar en el campo template, el tipo, el estado (activa/inactiva) y el contenido — un único bloque que debe traer las secciones [[subject]], [[text]] y [[html]] del metalenguaje de plantillas de Xamboo. La API de /v1/mail solo consume plantillas ya creadas ahí.
El tipo debe ser Correo electrónico — la consola también ofrece un tipo Layout de correo, pensado para envolver a otras plantillas (un encabezado/pie reutilizable que inyecta el cuerpo de la plantilla hija), no para enviarse solo. Si intentas usar el id de un layout directo en template, la API responde 3026.
Cómo referenciar tus vars dentro de la plantilla
Igual que en el envío ad-hoc: cada clave de to.vars se referencia directo, con su mismo nombre y sin ningún prefijo — {{clave}}. El contenido de la plantilla solo ve tus vars, nunca el resto del request (destinatario, remitente, asunto, etc.); si necesitas imprimir alguno de esos datos dentro del cuerpo, agrégalo también como var.
[[subject]]Recupera tu contraseña, {{nombre}}[[]]
[[text]]Tu código de recuperación es: {{codigo}}[[]]
[[html]]<p>Hola {{nombre}}, tu código es <strong>{{codigo}}</strong>.</p>[[]]
Contenido de plantilla de ejemplo para "template": "recuperacion" con las vars del request de arriba (nombre, codigo).
Plantillas en varios idiomas
Si tu correo va a distintos países, manda el campo lang junto con template — un código de idioma BCP-47 (fr, en, de, etc., sin distinguir mayúsculas ni región — fr-CA también sirve).
curl -s -X POST https://api.webability.info/v1/mail/send \
-H "X-WA-Client: $CLIENTID" -H "X-WA-Timestamp: $TS" -H "X-WA-Digest: $DIG" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "no-reply@tuempresa.com", "name": "Tu Empresa" },
"to": { "email": "cliente@ejemplo.com", "name": "Ana", "vars": { "nombre": "Ana", "codigo": "482913" } },
"template": "recuperacion",
"lang": "fr"
}'
Qué hace el servidor con lang
- Busca la variante ya traducida — el id template + "_" + lang (en el ejemplo, recuperacion_fr). Si existe y está activa, la usa directo.
- Si no existe, usa la plantilla base (recuperacion) y revisa su opción
auto-traducir (configurable en la consola, por plantilla):
- • Activada — genera la traducción con IA en ese momento, la guarda como recuperacion_fr para no volver a traducir la próxima vez, y la usa para este envío.
- • Desactivada, o si la traducción falla — usa la plantilla base tal cual (el correo se envía igual, en el idioma original).
En ningún caso lang hace fallar el envío: en el peor caso, degrada a enviar en el idioma de la plantilla base. lang se ignora por completo si no mandas template.
wait_send — envío síncrono
Por defecto /v1/mail/send responde de inmediato con el correo encolado (queue_status: "pending") — el envío real (conexión SMTP, entrega) sucede después, de forma asíncrona. Si necesitas saber el resultado real en el mismo request (por ejemplo, para decidir si reintentar con otro proveedor o marcar algo en tu base de datos), manda "wait_send": true.
Con wait_send: true, el servidor espera hasta 20 segundos el resultado real antes de responder. Si el envío se resuelve a tiempo, queue_status viene "sent" o "error" (con error_detail). Si no se resuelve a tiempo, la respuesta degrada a "pending" igual que sin la bandera — el queue_key sigue siendo válido para consultar después con GET /status.
curl -s -X POST https://api.webability.info/v1/mail/send \
-H "X-WA-Client: $CLIENTID" -H "X-WA-Timestamp: $TS" -H "X-WA-Digest: $DIG" \
-H "Content-Type: application/json" \
-d '{ "from": {...}, "to": {...}, "subject": "...", "html": "...", "wait_send": true }'
{ "status": "ok", "queue_key": 42, "queue_status": "sent", "to": "cliente@ejemplo.com" }
{ "status": "ok", "queue_key": 43, "queue_status": "error",
"error_detail": "550 5.1.1 The email account does not exist", "to": "no-existe@dominio.com" }
GET /v1/mail/status/{queue_key}
Consulta el estatus real de un envío hecho con POST /v1/mail/send. Solo el cliente dueño de la cuenta que lo encoló puede consultarlo — cualquier otro queue_key responde 404, sin distinguir "no existe" de "no es tuyo".
PATH="/v1/mail/status/42"
MSG="GET|${PATH}|$(date +%s)|${CLIENTID}"
DIG=$(echo -n "$MSG" | openssl dgst -sha256 -hmac "$TOKEN" | awk '{print $2}')
curl -s https://api.webability.info${PATH} \
-H "X-WA-Client: $CLIENTID" \
-H "X-WA-Timestamp: $(date +%s)" \
-H "X-WA-Digest: $DIG"
{ "status": "ok", "queue_key": 42, "queue_status": "sent" }
Valores de queue_status
| Valor | Significado |
|---|---|
| pending | Encolado, aún no se procesa. |
| processing | El mailer lo tomó de la cola y está enviándolo. |
| sent | Entregado al servidor destino sin errores. |
| error | Falló — revisa error_detail para el motivo (SMTP rechazado, dominio inexistente, buzón lleno, etc.). |
POST /v1/mail/send-bulk
Envía el mismo correo (con variables por destinatario) a varios destinatarios en un solo request. Cada destinatario se encola por separado — no soporta wait_send.
{
"from": { "email": "newsletter@tuempresa.com", "name": "Tu Empresa" },
"subject": "Novedades de la semana",
"html": "<p>Hola {{nombre}}!</p>",
"recipients": [
{ "email": "ana@ejemplo.com", "vars": { "nombre": "Ana" } },
{ "email": "luis@ejemplo.com", "vars": { "nombre": "Luis" } }
]
}
{
"status": "ok", "total": 2, "queued": 2, "failed": 0,
"results": [
{ "email": "ana@ejemplo.com", "queue_key": 10, "status": "queued" },
{ "email": "luis@ejemplo.com", "queue_key": 11, "status": "queued" }
]
}
Cada queue_key se puede consultar individualmente con GET /v1/mail/status/{queue_key}.
Errores
| Código | HTTP | Descripción |
|---|---|---|
| 3001 | 401 | Faltan headers de autenticación |
| 3002 | 401 | Firma inválida o timestamp expirado |
| 3003 | 401 | Cliente no encontrado |
| 3010 | 404 | Servicio desconocido (usa send / send-bulk / status) |
| 3011 | 405 | Método HTTP no soportado |
| 3020 | 400 | Body JSON inválido o vacío |
| 3021 | 400 | Campo requerido faltante (from.email, to.email o recipients) |
| 3022 | 500 | Error al encolar el correo |
| 3023 | 400 | Clave de cola inválida (GET status) |
| 3024 | 404 | Cola no encontrada, o no pertenece a este cliente (GET status) |
| 3025 | 404 | El template indicado no existe (o no pertenece a tu cuenta) |
| 3026 | 400 | El template indicado existe pero no está activo, o no es de tipo email |