E-mail transacional

core.transactional-email — configuração protegida, templates versionados, preview, envio idempotente, fila com retentativas e painel administrativo.

1. Declare a capability

{
  "capabilities": ["core.transactional-email"],
  "capabilitySettings": {
    "core.transactional-email": {
      "enabled": true,
      "scopePolicy": "tenant_override",
      "retentionDays": 30,
      "attachmentsPolicy": { "allowed": false },
      "templates": [
        { "code": "confirmacao", "definition": "./backend/emails/confirmacao.json", "initialStatus": "active" }
      ]
    }
  }
}
Configuração Valores
scopePolicy app_only, tenant_required ou tenant_override
retentionDays 1 a 90
attachmentsPolicy Anexos não são permitidos nesta linha
templates[].definition Caminho relativo ao manifesto, dentro de backend/ para ir na imagem

2. Crie o template

{
  "code": "confirmacao",
  "name": "Confirmação",
  "subject": "Olá, {{nome}}",
  "htmlContent": "<p>Olá, {{nome}}.</p>",
  "textContent": "Olá, {{nome}}.",
  "variablesSchema": {
    "type": "object",
    "additionalProperties": false,
    "required": ["nome"],
    "properties": { "nome": { "type": "string" } }
  },
  "locale": "pt-BR",
  "initialStatus": "active",
  "attachmentsPolicy": { "allowed": false }
}

Templates usam somente interpolação Handlebars escapada. Blocos, helpers, subexpressões, triple-stache e conteúdo remoto executável não são aceitos. O escape HTML vale só para htmlContent; assunto e texto saem literais (CR/LF são removidos do assunto).

3. Envie pelo backend

const { capabilities } = require("@oondemand/oon-core-back");

async function enviarConfirmacao({ email, nome, idempotencyKey }, req) {
  return capabilities.transactionalEmail.send(
    {
      templateCode: "confirmacao",
      to: email,
      variables: { nome },
      idempotencyKey,
      correlationId: req.correlationId,
      maxAttempts: 5,
    },
    { ...req.accessContext, userId: req.accessContext?.userId },
  );
}

module.exports = { enviarConfirmacao };

Retorno: { dispatchId, status, correlationId } com status igual a accepted ou duplicate.

O namespace capabilities.transactionalEmail também oferece preview e versionamento de templates, consulta de dispatches e retry de dead letter.

4. Painel administrativo

import { CoreTransactionalEmail } from "@oondemand/oon-core-front";

export function EmailPage() {
  return <CoreTransactionalEmail />;
}

Quando a capability está declarada, o Core já registra a página /configuracoes/email-transacional e o item de menu em Configurações. Conceda apenas as permissões necessárias:

Permissão Uso
transactional-email.config.read / .config.manage Ver / gerenciar provedor e configuração
transactional-email.templates.read / .templates.manage Ver / gerenciar templates
transactional-email.dispatches.read / .dispatches.retry Ver envios / reenviar dead letter

Credenciais do provedor são inseridas pela tela protegida e armazenadas pelo Core — nunca no manifesto, frontend, repositório ou log.

Segurança e operação

  • App e tenant são resolvidos do contexto validado;
  • tenant_required exige tenant; tenant_override usa a configuração do tenant e recua para a do App;
  • destinatários e variáveis são validados antes de enfileirar;
  • payload protegido tem retenção limitada e não aparece nas listagens;
  • erros usam TransactionalEmailError com códigos EMAIL_* sanitizados; sem declaração: EMAIL_CAPABILITY_NOT_DECLARED;
  • no runtime local, falha fechado até a capability estar configurada;
  • testes substituem a fronteira externa — nunca envie e-mail real em testes.