Pular para conteúdo

SES_SEND_TEMPLATE - Enviar Email com Template

O que é este Node?

O SES_SEND_TEMPLATE é o node responsável por enviar emails usando templates HTML pré-definidos no AWS SES com substituição dinâmica de variáveis, ideal para campanhas de marketing e emails transacionais padronizados.

Por que este Node existe?

Templates de email permitem reutilização e consistência. O SES_SEND_TEMPLATE existe para:

  1. Reutilização: Crie template uma vez, use milhares de vezes
  2. Consistência visual: Garante marca uniforme em todos emails
  3. Personalização em massa: Substitua variáveis como {{nome}}, {{pedido}}, etc.
  4. Governança: Equipe de marketing controla templates, devs só enviam dados
  5. Performance: Templates são pré-compilados pela AWS (envio mais rápido)
  6. Versionamento: AWS mantém histórico de versões de templates
  7. Campanhas de marketing: Envie 10.000 emails personalizados com um template

Como funciona internamente?

Quando o SES_SEND_TEMPLATE é executado, o sistema:

  1. Valida credenciais: Verifica accessKeyId, secretAccessKey e region
  2. Cria cliente SES: Inicializa SDK da AWS
  3. Valida template: Verifica se templateName existe no SES
  4. Valida remetente: Email "from" deve estar verificado
  5. Prepara templateData: Converte objeto de variáveis em JSON string
  6. Substitui variáveis: AWS processa {{variavel}} no template com valores fornecidos
  7. Envia email: Chama sendTemplatedEmail() da API SES
  8. Retorna messageId: ID único para rastreamento
  9. Se erro: Template não existe, variável faltando, limite atingido, etc.

Código interno (aws-executors.service.ts:414-426):

case 'sendTemplatedEmail':
  const templateResult = await ses.sendTemplatedEmail({
    Source: data.fromEmail,
    Destination: {
      ToAddresses: data.toEmails,
    },
    Template: data.templateName,
    TemplateData: JSON.stringify(data.templateData),
  }).promise();
  return {
    success: true,
    messageId: templateResult.MessageId,
  };

Quando você DEVE usar este Node?

Use SES_SEND_TEMPLATE sempre que precisar de emails padronizados com dados dinâmicos:

Casos de uso

  1. Newsletters: "Olá {{nome}}, confira as novidades de {{mes}}"
  2. Confirmações de pedido: Template padrão com {{pedido_numero}}, {{total}}, {{itens}}
  3. Emails de boas-vindas: Mesmo design para todos novos usuários
  4. Relatórios mensais: Template com {{metricas}}, {{grafico}}, {{periodo}}
  5. Campanhas promocionais: "{{nome}}, aproveite {{desconto}}% OFF em {{categoria}}"
  6. Lembretes: "{{nome}}, você tem {{quantidade}} itens no carrinho"
  7. Notificações de evento: "{{nome}}, seu evento {{evento_nome}} é amanhã"

Quando NÃO usar SES_SEND_TEMPLATE

  • Email único customizado: Use SES_SEND_EMAIL para mensagens únicas
  • Template muda frequentemente: Se HTML muda toda hora, não compensa criar template
  • Precisa anexos: Templates SES não suportam anexos (use sendEmail)
  • Email muito simples: "Obrigado!" não precisa de template

Como criar Templates no AWS SES

Método 1: AWS Console (Interface Gráfica)

  1. Acesse SES Console: https://console.aws.amazon.com/ses/
  2. Email Templates: Sidebar → Configuration → Email templates
  3. Create template: Clique em "Create template"
  4. Preencha:
  5. Template name: confirmacao-pedido
  6. Subject: Pedido {{pedido_numero}} Confirmado!
  7. HTML part:
    <html>
    <body>
      <h1>Olá {{nome}}!</h1>
      <p>Seu pedido <strong>{{pedido_numero}}</strong> foi confirmado.</p>
      <p>Total: {{total}}</p>
      <p>Itens: {{itens}}</p>
    </body>
    </html>
    
  8. Text part:
    Olá {{nome}}!
    
    Seu pedido {{pedido_numero}} foi confirmado.
    Total: {{total}}
    Itens: {{itens}}
    
  9. Create template: Salvar

Método 2: AWS CLI (Linha de Comando)

Crie arquivo template.json:

{
  "Template": {
    "TemplateName": "confirmacao-pedido",
    "SubjectPart": "Pedido {{pedido_numero}} Confirmado!",
    "TextPart": "Olá {{nome}}!\n\nSeu pedido {{pedido_numero}} foi confirmado.\nTotal: {{total}}",
    "HtmlPart": "<html><body><h1>Olá {{nome}}!</h1><p>Seu pedido <strong>{{pedido_numero}}</strong> foi confirmado.</p><p>Total: {{total}}</p></body></html>"
  }
}

Execute:

aws ses create-template --cli-input-json file://template.json --region us-east-1

Método 3: Via API (Programático)

import AWS from 'aws-sdk';

const ses = new AWS.SES({ region: 'us-east-1' });

await ses.createTemplate({
  Template: {
    TemplateName: 'confirmacao-pedido',
    SubjectPart: 'Pedido {{pedido_numero}} Confirmado!',
    HtmlPart: '<html>...</html>',
    TextPart: 'Olá {{nome}}...'
  }
}).promise();

Variáveis em Templates

Sintaxe

Use {{variavel}} no Subject, HtmlPart e TextPart:

<h1>Olá {{nome}}!</h1>
<p>Você ganhou {{pontos}} pontos!</p>
<p>Seu saldo é: R$ {{saldo}}</p>

No Flow, envie dados:

{
  "templateData": {
    "nome": "José Roberto",
    "pontos": "150",
    "saldo": "1.299,90"
  }
}

Resultado final:

<h1>Olá José Roberto!</h1>
<p>Você ganhou 150 pontos!</p>
<p>Seu saldo é: R$ 1.299,90</p>

Variável faltando?

Se template tem {{email}} mas você não enviou no templateData: - AWS deixa literal: "{{email}}" aparece no email - Recomendação: Sempre envie todas variáveis ou use valores default

Valores default no template:

AWS SES não suporta defaults nativamente. Solução: envie sempre:

{
  "templateData": {
    "nome": "{{nome}}",
    "sobrenome": "{{sobrenome || 'Não informado'}}"
  }
}

Melhor: Garanta valores no flow antes de enviar:

{
  "variable": {
    "nome": "{{nome || 'Cliente'}}",
    "sobrenome": "{{sobrenome || ''}}"
  }
}

Parâmetros Detalhados

accessKeyId (string, obrigatório)

O que é: Chave de acesso IAM da AWS com permissões SES.

Mesmas instruções de SES_SEND_EMAIL - veja documentação anterior.

secretAccessKey (string, obrigatório)

O que é: Chave secreta correspondente ao accessKeyId.

Segurança: Nunca exponha em código.

region (string, obrigatório)

O que é: Região AWS onde template foi criado.

Importante: Template deve existir na MESMA região que você está enviando.

Regiões comuns: - us-east-1 (Norte da Virgínia) - us-west-2 (Oregon) - eu-west-1 (Irlanda) - sa-east-1 (São Paulo)

fromEmail (string, obrigatório)

O que é: Email do remetente (deve estar verificado no SES).

Formatos: - sender@exemplo.com - Equipe Vendas <vendas@exemplo.com>

toEmails (array, obrigatório)

O que é: Lista de emails destinatários.

Formato: ["email1@exemplo.com", "email2@exemplo.com"]

Limite: Até 50 destinatários

Flow completo para testar:

{
  "name": "Teste SES Template - Múltiplos Destinatários",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Dados Newsletter",
        "parameters": {
          "variables": {
            "mes": "Janeiro",
            "destaque": "50% OFF em todos os produtos",
            "link": "https://loja.com.br/promo"
          }
        }
      }
    },
    {
      "id": "ses_1",
      "type": "aws_ses",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Enviar Newsletter",
        "parameters": {
          "operation": "sendTemplatedEmail",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "fromEmail": "newsletter@loja.com.br",
          "toEmails": ["cliente1@exemplo.com", "cliente2@exemplo.com", "cliente3@exemplo.com"],
          "templateName": "newsletter-mensal",
          "templateData": {
            "mes": "{{mes}}",
            "destaque": "{{destaque}}",
            "link": "{{link}}"
          }
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Newsletter de {{mes}} enviada para 3 clientes!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "ses_1" },
    { "source": "ses_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Todos 3 clientes recebem email personalizado com dados de Janeiro.

templateName (string, obrigatório)

O que é: Nome do template criado no SES.

Onde encontrar: - AWS Console → SES → Email templates - Lista todos templates disponíveis

Erro comum:

Template 'nome-errado' does not exist

Solução: Certifique que template foi criado na mesma região.

Flow completo para testar:

{
  "name": "Teste SES Template - Nome Template",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Qual Template?",
        "parameters": {
          "message": "Digite o nome do template (ex: boas-vindas):",
          "variable": "template_escolhido"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Dados",
        "parameters": {
          "variables": {
            "nome": "José Roberto",
            "empresa": "Lumina"
          }
        }
      }
    },
    {
      "id": "ses_1",
      "type": "aws_ses",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Enviar com Template",
        "parameters": {
          "operation": "sendTemplatedEmail",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "fromEmail": "noreply@lumina.app.br",
          "toEmails": ["teste@exemplo.com"],
          "templateName": "{{template_escolhido}}",
          "templateData": {
            "nome": "{{nome}}",
            "empresa": "{{empresa}}"
          }
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Email enviado usando template: {{template_escolhido}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "variable_1" },
    { "source": "variable_1", "target": "ses_1" },
    { "source": "ses_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Digite "boas-vindas" e sistema usa esse template. Digite nome inexistente e retorna erro.

templateData (object, obrigatório)

O que é: Objeto com variáveis que serão substituídas no template.

Formato:

{
  "templateData": {
    "variavel1": "valor1",
    "variavel2": "valor2",
    "numero": "123",
    "preco": "R$ 99,90"
  }
}

Importante: - Todos valores devem ser STRINGS (mesmo números) - Nome deve corresponder exatamente ao {{variavel}} no template - Não enviar variável = ela aparece literal no email

Exemplo:

Template tem:

<p>Olá {{nome}}! Você tem {{mensagens}} novas mensagens.</p>

Envie:

{
  "templateData": {
    "nome": "Maria",
    "mensagens": "5"
  }
}

Resultado:

<p>Olá Maria! Você tem 5 novas mensagens.</p>

Flow completo para testar variáveis complexas:

{
  "name": "Teste SES Template - Variáveis Complexas",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Dados Pedido Completo",
        "parameters": {
          "variables": {
            "cliente_nome": "José Roberto Silva",
            "cliente_email": "jose@empresa.com",
            "pedido_numero": "PED-2025-12345",
            "pedido_data": "15/01/2025",
            "pedido_status": "Confirmado",
            "item1_nome": "Mouse Gamer",
            "item1_qtd": "2",
            "item1_preco": "R$ 150,00",
            "item2_nome": "Teclado Mecânico",
            "item2_qtd": "1",
            "item2_preco": "R$ 450,00",
            "subtotal": "R$ 750,00",
            "frete": "R$ 25,00",
            "total": "R$ 775,00",
            "link_rastreamento": "https://rastreio.com/PED-2025-12345"
          }
        }
      }
    },
    {
      "id": "ses_1",
      "type": "aws_ses",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Enviar Pedido Detalhado",
        "parameters": {
          "operation": "sendTemplatedEmail",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "fromEmail": "vendas@loja.com.br",
          "toEmails": ["{{cliente_email}}"],
          "templateName": "confirmacao-pedido-detalhado",
          "templateData": {
            "cliente_nome": "{{cliente_nome}}",
            "pedido_numero": "{{pedido_numero}}",
            "pedido_data": "{{pedido_data}}",
            "pedido_status": "{{pedido_status}}",
            "item1_nome": "{{item1_nome}}",
            "item1_qtd": "{{item1_qtd}}",
            "item1_preco": "{{item1_preco}}",
            "item2_nome": "{{item2_nome}}",
            "item2_qtd": "{{item2_qtd}}",
            "item2_preco": "{{item2_preco}}",
            "subtotal": "{{subtotal}}",
            "frete": "{{frete}}",
            "total": "{{total}}",
            "link_rastreamento": "{{link_rastreamento}}"
          }
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Confirmação enviada para {{cliente_nome}} ({{pedido_numero}})"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "ses_1" },
    { "source": "ses_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Email enviado com todos detalhes do pedido formatados no template.

Parâmetros

Campo Tipo Obrigatório Descrição
operation string Sim Deve ser "sendTemplatedEmail"
accessKeyId string Sim AWS Access Key ID
secretAccessKey string Sim AWS Secret Access Key
region string Sim Região AWS do template
fromEmail string Sim Email remetente verificado
toEmails array Sim Lista de destinatários
templateName string Sim Nome do template no SES
templateData object Sim Variáveis do template

Exemplo 1: Newsletter Mensal

Objetivo: Enviar newsletter usando template padrão

Pré-requisito: Criar template "newsletter-mensal" no SES Console:

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; }
    .header { background: #007bff; color: white; padding: 20px; }
    .content { padding: 20px; }
    .destaque { background: #fffacd; padding: 15px; border-left: 4px solid #ffd700; }
    .footer { background: #f8f9fa; padding: 10px; text-align: center; color: #666; }
  </style>
</head>
<body>
  <div class="header">
    <h1>Newsletter {{mes}}</h1>
  </div>
  <div class="content">
    <p>Olá {{nome}}!</p>
    <div class="destaque">
      <h2>📢 Destaque do mês:</h2>
      <p>{{destaque}}</p>
      <a href="{{link}}" style="background: #28a745; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">Ver Promoção</a>
    </div>
    <p>Não perca essas ofertas incríveis!</p>
  </div>
  <div class="footer">
    <p>Você está recebendo porque se inscreveu em nossa newsletter.</p>
    <a href="{{link_unsubscribe}}">Cancelar inscrição</a>
  </div>
</body>
</html>

JSON para Importar

{
  "name": "Newsletter Mensal",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Dados Newsletter",
        "parameters": {
          "variables": {
            "mes": "Janeiro 2025",
            "nome": "José Roberto",
            "destaque": "Super Sale: 60% OFF em Eletrônicos!",
            "link": "https://loja.com.br/sale",
            "link_unsubscribe": "https://loja.com.br/unsubscribe?email=jose@exemplo.com"
          }
        }
      }
    },
    {
      "id": "ses_1",
      "type": "aws_ses",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Enviar Newsletter",
        "parameters": {
          "operation": "sendTemplatedEmail",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "fromEmail": "Marketing <newsletter@loja.com.br>",
          "toEmails": ["jose@exemplo.com"],
          "templateName": "newsletter-mensal",
          "templateData": {
            "mes": "{{mes}}",
            "nome": "{{nome}}",
            "destaque": "{{destaque}}",
            "link": "{{link}}",
            "link_unsubscribe": "{{link_unsubscribe}}"
          }
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Newsletter de {{mes}} enviada com sucesso!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "ses_1" },
    { "source": "ses_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Newsletter de Janeiro 2025 enviada com sucesso!

[Email recebido]
Assunto: Newsletter Janeiro 2025
Corpo: Design bonito com header azul, destaque amarelo, botão verde "Ver Promoção"

Exemplo 2: Confirmação de Cadastro com Template

Objetivo: Usar template para emails de boas-vindas

Template "boas-vindas" no SES:

Subject: Bem-vindo à {{empresa}}, {{nome}}!

HtmlPart:

<html>
<body style="font-family: Arial;">
  <h1 style="color: #28a745;">🎉 Bem-vindo à {{empresa}}!</h1>
  <p>Olá <strong>{{nome}}</strong>,</p>
  <p>Estamos muito felizes em ter você conosco!</p>
  <h3>Próximos passos:</h3>
  <ol>
    <li>Complete seu perfil: <a href="{{link_perfil}}">Clique aqui</a></li>
    <li>Conheça nossos recursos</li>
    <li>Comece a usar!</li>
  </ol>
  <p>Se tiver dúvidas, responda este email.</p>
  <p>Abraços,<br>Equipe {{empresa}}</p>
</body>
</html>

JSON para Importar

{
  "name": "Cadastro com Template",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Boas-vindas",
        "parameters": {
          "message": "Bem-vindo! Vamos criar sua conta."
        }
      }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Nome",
        "parameters": {
          "message": "Qual é o seu nome completo?",
          "variable": "nome"
        }
      }
    },
    {
      "id": "email_1",
      "type": "email",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Email",
        "parameters": {
          "message": "Qual é o seu email?",
          "variable": "email"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Preparar Dados",
        "parameters": {
          "variables": {
            "empresa": "Lumina",
            "link_perfil": "https://lumina.app.br/perfil"
          }
        }
      }
    },
    {
      "id": "ses_1",
      "type": "aws_ses",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Enviar Boas-vindas",
        "parameters": {
          "operation": "sendTemplatedEmail",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "fromEmail": "Equipe Lumina <noreply@lumina.app.br>",
          "toEmails": ["{{email}}"],
          "templateName": "boas-vindas",
          "templateData": {
            "nome": "{{nome}}",
            "empresa": "{{empresa}}",
            "link_perfil": "{{link_perfil}}"
          }
        }
      }
    },
    {
      "id": "message_2",
      "type": "message",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Finalizar",
        "parameters": {
          "message": "Cadastro concluído, {{nome}}! Enviamos instruções para {{email}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1500, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "message_1" },
    { "source": "message_1", "target": "input_1" },
    { "source": "input_1", "target": "email_1" },
    { "source": "email_1", "target": "variable_1" },
    { "source": "variable_1", "target": "ses_1" },
    { "source": "ses_1", "target": "message_2" },
    { "source": "message_2", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Bem-vindo! Vamos criar sua conta.
Sistema: Qual é o seu nome completo?
Usuário: Maria Souza
Sistema: Qual é o seu email?
Usuário: maria@exemplo.com
Sistema: Cadastro concluído, Maria Souza! Enviamos instruções para maria@exemplo.com

[Email recebido]
Assunto: Bem-vindo à Lumina, Maria Souza!
Corpo: Template formatado com lista de próximos passos e links

Exemplo 3: Campanha Marketing em Massa

Objetivo: Enviar promoção personalizada para lista de clientes

JSON para Importar

{
  "name": "Campanha Black Friday",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Lista Clientes",
        "parameters": {
          "variables": {
            "clientes": [
              {"nome": "João Silva", "email": "joao@exemplo.com", "cupom": "JOAO50"},
              {"nome": "Ana Costa", "email": "ana@exemplo.com", "cupom": "ANA50"},
              {"nome": "Pedro Lima", "email": "pedro@exemplo.com", "cupom": "PEDRO50"}
            ]
          }
        }
      }
    },
    {
      "id": "loop_1",
      "type": "loop",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Para Cada Cliente",
        "parameters": {
          "array": "{{clientes}}",
          "itemVariable": "cliente"
        }
      }
    },
    {
      "id": "ses_1",
      "type": "aws_ses",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Enviar Promoção",
        "parameters": {
          "operation": "sendTemplatedEmail",
          "accessKeyId": "SUA_ACCESS_KEY",
          "secretAccessKey": "SUA_SECRET_KEY",
          "region": "us-east-1",
          "fromEmail": "Promoções <promo@loja.com.br>",
          "toEmails": ["{{cliente.email}}"],
          "templateName": "black-friday",
          "templateData": {
            "nome": "{{cliente.nome}}",
            "cupom": "{{cliente.cupom}}",
            "desconto": "50",
            "validade": "30/11/2025",
            "link": "https://loja.com.br/bf?cupom={{cliente.cupom}}"
          }
        }
      }
    },
    {
      "id": "delay_1",
      "type": "delay",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Aguardar 1s",
        "parameters": {
          "duration": 1000
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Campanha Black Friday enviada para todos os clientes!"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1300, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "loop_1" },
    { "source": "loop_1", "target": "ses_1" },
    { "source": "ses_1", "target": "delay_1" },
    { "source": "delay_1", "target": "loop_1" },
    { "source": "loop_1", "target": "message_1", "condition": "loop_end" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Campanha Black Friday enviada para todos os clientes!

[João recebe]
Assunto: João Silva, 50% OFF exclusivo para você!
Cupom: JOAO50

[Ana recebe]
Assunto: Ana Costa, 50% OFF exclusivo para você!
Cupom: ANA50

[Pedro recebe]
Assunto: Pedro Lima, 50% OFF exclusivo para você!
Cupom: PEDRO50

Nota: Delay de 1s entre envios para respeitar limites do SES (14 emails/segundo em production).

Resposta do Node

{
  "success": true,
  "messageId": "0100018d8e8a7c9e-a1b2c3d4-5678-90ab-cdef-1234567890ab-000000"
}

messageId: ID único para rastreamento (mesmo uso de SES_SEND_EMAIL).

Gerenciar Templates SES

Listar templates existentes:

aws ses list-templates --region us-east-1

Ver detalhes de um template:

aws ses get-template --template-name nome-template --region us-east-1

Atualizar template:

aws ses update-template --cli-input-json file://template-atualizado.json

Deletar template:

aws ses delete-template --template-name nome-template --region us-east-1

Versionamento de Templates

AWS não mantém versões automaticamente. Recomendações:

  1. Nomear com versão: confirmacao-pedido-v2, confirmacao-pedido-v3
  2. Backup em Git: Salve HTMLs dos templates no repositório
  3. Documentar mudanças: README explicando cada versão
  4. Testar antes de atualizar: Use template de teste primeiro

Boas Práticas

SIM:

  • Crie templates reutilizáveis e genéricos
  • Use nomes descritivos: confirmacao-pedido, não template1
  • Sempre inclua TextPart e HtmlPart no template
  • Teste template com dados de exemplo antes de usar em produção
  • Documente todas variáveis necessárias (crie README ou comentário)
  • Use CSS inline (não <link> externo)
  • Adicione link de unsubscribe em emails de marketing
  • Mantenha backup dos templates em repositório Git
  • Use variáveis para tudo que muda: datas, nomes, valores
  • Versionamento: newsletter-v1, newsletter-v2 se mudar muito

NÃO:

  • Não crie template para cada email único (perde o propósito)
  • Não use JavaScript em templates (AWS remove)
  • Não esqueça de enviar TODAS as variáveis no templateData
  • Não hardcode dados que mudam (use variáveis)
  • Não use templates sem testar antes
  • Não delete templates em uso (pode quebrar flows ativos)
  • Não crie templates em região diferente de onde vai enviar
  • Não envie templateData com tipos errados (só strings)

Dicas

💡 Teste variáveis: Crie template de teste com todas variáveis visíveis para debug

💡 Fallback: Se variável pode estar vazia, ajuste HTML para não quebrar layout

💡 Performance: Templates são pré-compilados - 30% mais rápido que sendEmail com HTML inline

💡 Custo: Mesmo custo de sendEmail ($0.10/1000 emails)

💡 Limite de templates: 10.000 templates por conta AWS (mais que suficiente)

💡 Organização: Use prefixos: trans- para transacionais, mkt- para marketing

💡 Aprovação: Marketing aprova template, devs só enviam dados (separação de responsabilidades)

💡 A/B Testing: Crie 2 templates diferentes e alterne para testar qual converte melhor

Próximo Node

SES_SEND_EMAIL - Enviar email sem template → SES_VERIFY_EMAIL - Verificar email no SES → SNS_PUBLISH - Publicar mensagem no SNS