Pular para o conteúdo

Padrão de e-mail

Status: canônico · Escopo: Como escrever e enviar e-mail transacional da Tactflow · Atualizado em: 2026-08-10

Cada regra aqui saiu de um defeito real, e a maioria só apareceu depois de mandar o e-mail de verdade e olhar como quem recebe. Teste com fetch falso passou em todos eles.


o quêonde
Cabeçalho, cores, cartão, rodapéapps/api-worker/src/notifications/emails/layout.ts
Um e-mail específicoapps/api-worker/src/notifications/emails/<nome>.ts
O envioapps/api-worker/src/notifications/resend-email.ts

Existem hoje: booking-link (o prospect escolhe horário) e owner-booking (o dono é avisado de um agendamento novo). O segundo é a coisa mais parecida com o produto que o cliente compra — ele não liga para o agente, liga para saber que entrou trabalho.

E-mail novo monta o conteúdo e chama montarEmail. Não reescreve cabeçalho nem rodapé — mexer no layout muda todos de uma vez, que é o ponto de ele existir.


elec #6e9bff SÓ sobre navy
blue #2762eb SÓ sobre paper/branco

Não é gosto. elec sobre branco some, e é a primeira coisa que denuncia e-mail montado às pressas. A fonte é global.css do site.

O gesto ousado fica em um lugar por e-mail — o número em elec sobre navy, ou o botão em blue. Dois blocos berrantes disputando atenção viram nenhum.


text é obrigatório mesmo quando existe html. Duas razões, e as duas custam caro:

  • Mensagem só-HTML é sinal clássico de spam para Gmail e Outlook, e quem paga é o domínio inteiro, não só aquele envio.
  • Quem lê em texto puro precisa conseguir agir. Se a única saída é um botão, essa pessoa fica sem saída — todo link do HTML tem que aparecer como URL no texto.

  • Sem valor financeiro. $18,720 a year in calls you never picked up muda o registro de “aqui está o que você pediu” para “olha essa oferta”, e o e-mail deixa de parecer o que é.
  • Reconhecível na primeira olhada. O primeiro e-mail que um desconhecido recebe chega com o aviso “você geralmente não recebe e-mails deste remetente” no Outlook. Não dá para remover — dá para o assunto responder “isso é o que eu pedi” antes de a pessoa decidir se confia.

⚠️ A regra que mais rendeu defeito. O mesmo passo de contato tem duas portas:

portao que a pessoa viu
funilsetor, ligações perdidas, valor do serviço, taxa de fechamento
formulário ou /booknada

O e-mail abria com “thanks for going through the numbers” para os dois. Para quem veio pelo formulário, isso cita algo que nunca aconteceu — e o efeito é pior que texto genérico: parece e-mail trocado, ou automação mal feita.

Todo e-mail que muda de contexto recebe a informação de contexto como parâmetro e tem teste para os dois caminhos. Ver booking-link.ts (sawBreakdown).


chat.tactflow.io página de agendamento, embed — o que uma PESSOA abre
api.tactflow.io webhook, callback de OAuth — o que uma MÁQUINA chama

Use resolveUiOrigin(env) em e-mail, nunca resolveAppOrigin. Um dono de HVAC lê o link antes de clicar, e “api” não diz nada para ele.


7. Visualização que precisa de legenda não entra

Seção intitulada “7. Visualização que precisa de legenda não entra”

Duas tentativas de mostrar “5 de 35 ligações eram trabalho” falharam:

  • Barra proporcional — 86% preenchida com um pedaço claro no fim lia como barra de progresso quase completa, que é sinal positivo. O significado era o oposto.
  • 35 quadradinhos — viraram padrão decorativo, e a pergunta que surgiu foi “o que é isso?”.

A frase sozinha já entregava. Se o leitor precisa da legenda para entender o gráfico, o gráfico está pedindo trabalho dele — e a legenda já era a resposta.


O Outlook renderiza com o motor do Word e ignora boa parte do CSS moderno. <table> com style="..." em cada célula é feio de escrever e previsível de receber.

Sem <style> no <head>: vários clientes removem. Sem webfont: não carrega de forma confiável — a pilha começa com Bricolage Grotesque para quem já a tem instalada e cai na sans do sistema.

Imagem: só o logo, servido de tactflow.io, e em PNG. SVG não renderiza no Gmail. Nada essencial pode depender de imagem carregar — metade dos clientes bloqueia por padrão, e é por isso que “Tactflow” ao lado do logo é texto.


O e-mail sai depois de gravar o que importa, e a falha dele vai para o log sem mudar o que a tela mostra.

Contato é o que não dá para recuperar depois. Perder um lead porque o serviço de e-mail caiu seria trocar um problema pequeno por um irreversível.


⚠️ Teste com fetch falso não prova que o e-mail chega, nem que ele faz sentido.

Os dois defeitos mais caros deste arquivo passaram por todos os testes automáticos:

  • o remetente estava em @send.tactflow.io e o Resend recusou com 403 domain is not verified — só o envio real disse isso;
  • a abertura citava números para quem nunca viu número — só apareceu quando um humano leu.

Para extrair o conteúdo real do módulo (e não uma cópia) e enviar:

Terminal window
# teste temporário que despeja o e-mail montado
pnpm --filter @voltron/api-worker exec vitest run src/notifications/emails/_preview.test.ts
# depois: POST para https://api.resend.com/emails com o JSON gerado

Conferir na caixa: caiu na entrada ou no spam, e no cabeçalho (Gmail → Mostrar original) se SPF, DKIM e DMARC estão todos em PASS.


registroestado
SPF (send.tactflow.io)include:amazonses.com
DKIM (resend._domainkey)
DMARC (_dmarc)p=none desde 10/08/2026

⚠️ O domínio verificado no Resend é tactflow.io, e é de lá que se envia. O send.tactflow.io é o return-path — é contra ele que o SPF é conferido, e por isso ele aponta para o Resend enquanto a raiz aponta para o Google Workspace, que recebe o e-mail humano. Quem alinha o DMARC aqui é o DKIM, não o SPF.

O DMARC está em modo monitoramento de propósito: com dois remetentes no domínio, subir para quarantine antes de confirmar alinhamento derrubaria e-mail legítimo. Falta criar o alias dmarc@tactflow.io para receber os relatórios.