Ir al contenido

Email

Cada empresa (tenant) define su propio servidor SMTP para enviar emails desde Keirost. Esto permite usar el dominio de la empresa como remitente.

GET /api/email/config
PATCH /api/email/config
{
"enabled": true,
"host": "smtp.gmail.com",
"port": 587,
"secure": false,
"user": "noreply@tudominio.com",
"password": "app_password_aqui",
"from_name": "Keirost - Tu Empresa",
"from_email": "noreply@tudominio.com",
"reply_to": "soporte@tudominio.com",
"tls": "starttls"
}
Campo Descripción
host Servidor SMTP (smtp.gmail.com, smtp.office365.com, smtp.tudominio.com)
port Puerto: 587 (STARTTLS), 465 (SMTPS), 25 (sin cifrar)
secure true = usa SSL/TLS directamente (puerto 465)
user Usuario o dirección de email remitente
password Password o app password (recomendado: app password, no password real)
from_name Nombre que aparece como remitente
from_email Email que aparece como remitente
reply_to Email de respuesta (si diferente del remitente)
tls starttls (recomendado), tls (SSL), none
POST /api/email/config/test
Content-Type: application/json
{
"to": "test@tudominio.com"
}

Envia un email de prueba a la dirección indicada. Devuelve success o el error de conexion.


Los emails no se envian de forma sincrona. Se encolan en segundo plano para no bloquear la interfaz y poder reintentarlos en caso de fallo.

interface EmailQueueItem {
id: string;
to: string[];
cc?: string[];
bcc?: string[];
subject: string;
body_html: string;
body_text?: string;
attachments?: { filename: string; path: string; size: number }[];
status: 'queued' | 'sending' | 'sent' | 'failed' | 'retrying';
attempts: number;
max_attempts: number;
last_error?: string;
sent_at?: string;
created_at: string;
created_by: string;
}
Estado Descripción
queued En espera. El siguiente proceso de cola lo recogerá.
sending Enviando actualmente.
sent Enviado correctamente.
failed Fallo tras agotar todos los reintentos.
retrying Reintentando tras un fallo temporal.
  • Máximo de intentos: 5
  • Intervalo de reintento: exponencial (1min, 5min, 15min, 1h, 4h)
  • Solo se reintenta en fallos temporales (timeout, conexion rechazada, servidor ocupado)
  • Fallos definitivos (autenticación incorrecta, email invalido) no se reintentan

POST /api/email/send
Authorization: Bearer <token>
Content-Type: application/json
{
"document_type": "sales_invoice",
"document_id": "inv_2026_0012",
"to": ["cliente@acme.com"],
"cc": ["contabilidad@acme.com"],
"subject": "Factura F-2026-0012",
"body_html": "<p>Estimado cliente, adjuntamos la factura {{document_number}}.</p>",
"attach_pdf": true
}

Respuesta inmediata (el email se encola):

{
"queue_id": "eq_001",
"status": "queued",
"message": "Email encolado. Recibirás una notificacion cuando se complete."
}

Antes de enviar, puedes ver exactamente como quedará:

POST /api/email/preview
Authorization: Bearer <token>
Content-Type: application/json
{
"document_type": "sales_invoice",
"document_id": "inv_2026_0012",
"to": ["cliente@acme.com"],
"subject": "Factura F-2026-0012",
"body_html": "<p>Estimado cliente, adjuntamos la factura {{document_number}}.</p>"
}

Devuelve:

{
"subject": "Factura F-2026-0012",
"from": "noreply@tudominio.com",
"to": ["cliente@acme.com"],
"body_html_rendered": "<p>Estimado cliente, adjuntamos la factura F-2026-0012.</p>",
"attachments": [
{
"filename": "F-2026-0012.pdf",
"size": 45678
}
],
"preview_url": "/api/email/preview/:queue_id"
}

Por defecto, cuando attach_pdf: true:

  • Se genera el PDF del documento con la plantilla activa
  • Se adjunta con el nombre {{series}}-{{number}}.pdf

También puedes adjuntar archivos adicionales (contratos, presupuestos, etc.):

{
"attachments": [
{ "path": "/storage/contracts/CTR-2026-001.pdf" },
{ "path": "/storage/presupuestos/PRE-2026-001.pdf" }
]
}

POST /api/email/send-bulk
Authorization: Bearer <token>
Content-Type: application/json
{
"document_type": "sales_invoice",
"document_ids": ["inv_2026_001", "inv_2026_002", "inv_2026_003"],
"to_field": "partner.email",
"subject": "Factura {{series}}-{{number}}",
"body_html": "<p>Estimado {{partner_name}}, adjuntamos su factura {{document_number}} del {{document_date}}.</p>",
"attach_pdf": true
}
Campo Descripción
document_ids Lista de IDs de documentos a enviar
to_field Campo del documento/partner usado como destinatario
subject Asunto con variables ({{series}}, {{number}}, {{partner_name}})
body_html Cuerpo HTML con variables

Respuesta:

{
"queued": 3,
"failed": 0,
"queue_ids": ["eq_001", "eq_002", "eq_003"],
"message": "3 emails encolados para envio"
}

GET /api/email/history?from=2026-01-01&to=2026-05-08&status=sent
{
"data": [
{
"id": "eq_001",
"to": ["cliente@acme.com"],
"subject": "Factura F-2026-0012",
"document_type": "sales_invoice",
"document_id": "inv_2026_0012",
"status": "sent",
"attempts": 1,
"sent_at": "2026-05-08T10:05:00Z",
"created_by": "admin"
}
],
"pagination": {
"total": 156,
"page": 1,
"per_page": 20
}
}
GET /api/email/history/:id

Incluye el contenido HTML completo, adjuntos y eventos de reintento.


Cuando un email termina de enviarse (o falla después de todos los reintentos), se genera una notificación in-app dirigida al usuario que lo envió.

GET /api/notifications?type=email_status
{
"data": [
{
"id": "notif_001",
"type": "email_status",
"title": "Email enviado",
"body": "Factura F-2026-0012 enviada a cliente@acme.com",
"read": false,
"created_at": "2026-05-08T10:05:00Z"
}
]
}

El icono de campana en la interfaz muestra un indicador pulsante cuando hay nuevas notificaciones de email.


Keirost permite crear plantillas de email reutilizables por tipo de documento.

GET /api/email/templates
POST /api/email/templates
PATCH /api/email/templates/:id
DELETE /api/email/templates/:id
{
"id": "et_factura_cliente",
"name": "Factura a cliente",
"document_type": "sales_invoice",
"subject": "Factura {{series}}-{{number}}",
"body_html": "<h2>Estimado {{partner_name}},</h2><p>Adjuntamos su factura <strong>{{document_number}}</strong> por importe de <strong>{{total}} EUR</strong>.</p>",
"attach_pdf": true,
"active": true
}
Variable Descripción
{{document_number}} Número completo del documento
{{document_date}} Fecha en formato legible
{{partner_name}} Nombre del cliente/proveedor
{{partner_vat}} CIF/NIF del partner
{{subtotal}} Importe sin impuestos
{{tax_amount}} Importe de impuestos
{{total}} Total con impuestos
{{due_date}} Fecha de vencimiento
{{company_name}} Nombre de la empresa
{{company_address}} Dirección de la empresa

Método Ruta Descripción
GET /api/email/config Obtener config SMTP
PATCH /api/email/config Guardar config SMTP
POST /api/email/config/test Enviar email de prueba
POST /api/email/send Enviar un documento por email
POST /api/email/preview Preview antes de enviar
POST /api/email/send-bulk Envío masivo
GET /api/email/history Historial de emails enviados
GET /api/email/history/:id Detalle de un email
GET /api/email/templates Listar plantillas
POST /api/email/templates Crear plantilla
PATCH /api/email/templates/:id Actualizar plantilla
DELETE /api/email/templates/:id Eliminar plantilla