--- id: INT-010 type: Integration title: "Email Multi-Provider" provider: "SendGrid/SES/SMTP" status: Planificado integration_type: "notifications" created_at: 2026-01-10 updated_at: 2026-01-10 simco_version: "4.0.1" tags: - email - notifications - transactional - multi-provider --- # INT-010: Email Multi-Provider ## Metadata | Campo | Valor | |-------|-------| | **Codigo** | INT-010 | | **Proveedor** | SendGrid, AWS SES, SMTP | | **Tipo** | Notificaciones | | **Estado** | Planificado | | **Multi-tenant** | Si | | **Epic Relacionada** | MCH-029 | | **Owner** | Backend Team | --- ## 1. Descripcion Sistema de envio de emails transaccionales con soporte para multiples proveedores y fallback automatico. Incluye templates reutilizables, tracking de entrega y rate limiting por tenant. **Casos de uso principales:** - Emails de bienvenida y verificacion - Notificaciones de pedidos - Recordatorios de pago - Alertas de inventario bajo - Reportes programados --- ## 2. Credenciales Requeridas ### Variables de Entorno | Variable | Descripcion | Tipo | Obligatorio | |----------|-------------|------|-------------| | `EMAIL_PROVIDER` | Proveedor principal (sendgrid/ses/smtp) | string | SI | | `SENDGRID_API_KEY` | API Key de SendGrid | string | SI (si usa SendGrid) | | `AWS_SES_REGION` | Region de AWS SES | string | SI (si usa SES) | | `AWS_SES_ACCESS_KEY` | Access Key para SES | string | SI (si usa SES) | | `AWS_SES_SECRET_KEY` | Secret Key para SES | string | SI (si usa SES) | | `SMTP_HOST` | Host del servidor SMTP | string | SI (si usa SMTP) | | `SMTP_PORT` | Puerto SMTP | number | SI (si usa SMTP) | | `SMTP_USER` | Usuario SMTP | string | SI (si usa SMTP) | | `SMTP_PASS` | Password SMTP | string | SI (si usa SMTP) | | `EMAIL_FROM_ADDRESS` | Email de remitente | string | SI | | `EMAIL_FROM_NAME` | Nombre de remitente | string | SI | ### Ejemplo de .env ```env # Email Configuration EMAIL_PROVIDER=sendgrid EMAIL_FROM_ADDRESS=noreply@michangarrito.com EMAIL_FROM_NAME=MiChangarrito # SendGrid SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxx # AWS SES (fallback) AWS_SES_REGION=us-east-1 AWS_SES_ACCESS_KEY=AKIAXXXXXXXXXXXXXXXX AWS_SES_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # SMTP (ultimo recurso) SMTP_HOST=smtp.mailtrap.io SMTP_PORT=587 SMTP_USER=xxxxxxxx SMTP_PASS=xxxxxxxx ``` --- ## 3. Endpoints/SDK Utilizados ### SendGrid | Operacion | Metodo | Endpoint | Descripcion | |-----------|--------|----------|-------------| | Enviar email | POST | `/v3/mail/send` | Envio simple o con template | | Templates | GET | `/v3/templates` | Listar templates | ### AWS SES | Operacion | SDK Method | Descripcion | |-----------|------------|-------------| | Enviar email | `sendEmail()` | Via AWS SDK v3 | | Enviar raw | `sendRawEmail()` | Con attachments | ### SMTP ```typescript import * as nodemailer from 'nodemailer'; const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: parseInt(process.env.SMTP_PORT), auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, }, }); ``` --- ## 4. Rate Limits | Proveedor | Limite | Periodo | Plan SendGrid | |-----------|--------|---------|---------------| | SendGrid | 100 | por segundo | Pro | | SendGrid | 40,000 | por mes | Free | | AWS SES | 14 | por segundo | Default | | AWS SES | 50,000 | por dia | Verified | ### Rate Limiting por Tenant | Plan MCH | Emails/hora | Emails/dia | |----------|-------------|------------| | Basic | 100 | 500 | | Pro | 1,000 | 10,000 | | Enterprise | Ilimitado | Ilimitado | --- ## 5. Manejo de Errores | Codigo | Descripcion | Accion | Retry | |--------|-------------|--------|-------| | 400 | Email invalido | Validar formato | NO | | 401 | API Key invalida | Verificar credencial | NO | | 429 | Rate limit | Esperar + retry | SI | | 500 | Error proveedor | Fallback a siguiente | SI | | 503 | Servicio no disponible | Fallback | SI | ### Estrategia de Fallback ```typescript const providers = ['sendgrid', 'ses', 'smtp']; async function sendWithFallback(email: EmailDto) { for (const provider of providers) { try { return await this.send(provider, email); } catch (error) { this.logger.warn(`Provider ${provider} failed, trying next`); continue; } } throw new Error('All email providers failed'); } ``` --- ## 6. Templates ### Templates Disponibles | Template | Descripcion | Variables | |----------|-------------|-----------| | welcome | Bienvenida | `{name}` | | verify_email | Verificacion | `{name}, {link}` | | order_confirmation | Pedido confirmado | `{order_id}, {items}` | | payment_reminder | Recordatorio pago | `{amount}, {due_date}` | | low_inventory | Alerta inventario | `{product}, {quantity}` | ### Formato de Template (Handlebars) ```html

Hola {{name}}

Tu pedido #{{order_id}} ha sido confirmado.

{{#each items}}
  • {{this.name}} x {{this.quantity}}
  • {{/each}} ``` --- ## 7. Multi-tenant ### Modelo de Credenciales - [x] **Global:** Credenciales compartidas por defecto - [x] **Por Tenant:** Tenant puede configurar sus propias credenciales - [x] **Branding:** From address personalizable por tenant (Enterprise) ### Almacenamiento ```sql CREATE TABLE messaging.tenant_email_config ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID REFERENCES auth.tenants(id) NOT NULL, provider VARCHAR(20) NOT NULL, -- sendgrid, ses, smtp from_address VARCHAR(255) NOT NULL, from_name VARCHAR(100), credentials JSONB NOT NULL, -- Encriptado is_active BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(tenant_id) ); ``` --- ## 8. Tablas de BD ### email_templates ```sql CREATE TABLE messaging.email_templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID REFERENCES auth.tenants(id), key VARCHAR(100) NOT NULL, name VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, html_content TEXT NOT NULL, text_content TEXT, variables JSONB, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(tenant_id, key) ); ``` ### email_logs ```sql CREATE TABLE messaging.email_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, template_id UUID REFERENCES messaging.email_templates(id), to_address VARCHAR(255) NOT NULL, subject VARCHAR(255) NOT NULL, provider VARCHAR(20) NOT NULL, provider_message_id VARCHAR(255), status VARCHAR(20) NOT NULL, -- sent, delivered, opened, clicked, bounced, failed error_message TEXT, sent_at TIMESTAMP WITH TIME ZONE, delivered_at TIMESTAMP WITH TIME ZONE, opened_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); ``` --- ## 9. Testing ### Modo Sandbox | Ambiente | Proveedor | Comportamiento | |----------|-----------|----------------| | Development | Mailtrap | Emails atrapados, no enviados | | Staging | SendGrid Sandbox | Emails simulados | | Production | SendGrid/SES | Envio real | ### Test de Conexion ```bash # Test SendGrid curl -X POST "https://api.sendgrid.com/v3/mail/send" \ -H "Authorization: Bearer $SENDGRID_API_KEY" \ -H "Content-Type: application/json" \ -d '{"personalizations":[{"to":[{"email":"test@example.com"}]}],"from":{"email":"noreply@michangarrito.com"},"subject":"Test","content":[{"type":"text/plain","value":"Test email"}]}' ``` --- ## 10. Monitoreo ### Metricas | Metrica | Descripcion | Alerta | |---------|-------------|--------| | email_sent_total | Emails enviados | - | | email_failed_total | Emails fallidos | > 5% | | email_delivery_rate | Tasa de entrega | < 95% | | email_bounce_rate | Tasa de rebote | > 5% | --- ## 11. Referencias - [SendGrid API Docs](https://docs.sendgrid.com/api-reference) - [AWS SES Developer Guide](https://docs.aws.amazon.com/ses/) - [Nodemailer Docs](https://nodemailer.com/) --- **Ultima actualizacion:** 2026-01-10 **Autor:** Backend Team