Selo de estado:
Preview(teto atual do produto) · Curso ACD-420 — Analista de IA: relatórios, alertas, anomalias e previsões · Aula 3 de 9 · Atualizado em 2026-10-04.
Objetivo#
Ao final desta aula você vai rastrear entrega até o recibo — e distinguir "enviado" de "entregue" sem adivinhar.
Vídeo#
Identificador no manifesto: acd-420-03-entrega-email-whatsapp-ledger · duração-alvo 7 min · tela do produto: /relatorios.
Roteiro de gravação (5 capítulos):
| # | Minutagem-alvo | Capítulo | Tela do produto |
|---|---|---|---|
| 1 | 0:00–0:45 | Abertura: título + selo Preview. Por que "mandei" não é "chegou", e o que um recibo resolve. | /relatorios/[id] |
| 2 | 0:45–2:30 | A máquina de estados do recibo: queued → sent → delivered; failed + próxima tentativa; dlq no teto. | /relatorios/[id] |
| 3 | 2:30–4:00 | Idempotência da entrega: a chave por origem/canal/destino; reenvio do mesmo artefato não cria segundo recibo. | /relatorios/[id] |
| 4 | 4:00–5:45 | Os dois canais: e-mail (domínio verificado) e WhatsApp (Cloud API oficial, template aprovado + consentimento). | /relatorios/novo |
| 5 | 5:45–7:00 | Falha que retry não conserta, reprocesso e os gates humanos. Encerramento com o "faça você mesmo". | /relatorios/[id] |
Conteúdo#
Por que existe um recibo
Antes do ledger, a entrega era silenciosa: uma falha de e-mail morria num campo JSON do run, sem id do provedor, sem contagem de tentativas, sem fila de reprocesso. O ledger de entrega resolve isso com um recibo por envio — origem (relatório, painel ou alerta), canal, destino, estado, número de tentativas, id no provedor e a próxima tentativa quando houver.
A máquina de estados é curta e vale decorar:
queued ──envio ok──────────────► sent ──(confirmação do provedor)──► delivered
│ └─falhou e vale re-tentar──► failed + próxima tentativa ──teto (3)──► dlq
└────canal não configurado────► failed SEM próxima tentativa| Estado | Significa | O que fazer |
|---|---|---|
queued | o envio foi enfileirado | esperar; recibo parado muito tempo volta ao ciclo pelo dispatcher |
sent | o canal aceitou a mensagem | não é prova de que chegou |
delivered | houve confirmação de entrega | é o único estado que significa "chegou" |
failed | falhou; se tiver próxima tentativa, volta | olhar o erro antes de reprocessar |
dlq | esgotou as 3 execuções de envio | investigar a causa; reprocessar não resolve sozinho |
O detalhe que mais confunde na operação: canal não configurado não é falha transiente. Se não há credencial de e-mail ou de WhatsApp, o recibo vai para failed sem próxima tentativa — porque re-tentar não conserta uma variável de ambiente ausente. Já falha de rede ou erro 5xx do provedor é transiente: entra no backoff exponencial com jitter determinístico e volta até o teto de 3.
Idempotência: o mesmo artefato, um recibo
A chave de idempotência da entrega é um hash de (origem, id da origem, canal, destino e o artefato), com UNIQUE no banco servindo de ponto de serialização. Reenviar o mesmo artefato para o mesmo destino não cria um segundo recibo: a tentativa colide, recebe o recibo existente e o produto decide se o envio acontece de novo. Corrida de crons, run reprocessado, retry manual — nada disso duplica entrega.
É a mesma ideia da idempotência de alerta da Aula 4, com uma diferença: lá a chave inclui o refreshedAt do snapshot; aqui, o artefato entregue.
Onde isso aparece na tela
No detalhe do relatório (/relatorios/[id]), a coluna Entregas do Histórico de execuções resume os recibos daquela execução: "2 ok", "2 ok · 1 falha(s)" ou — quando não houve envio. É daí que você parte: a execução pode estar Sucesso e a entrega ter falhado — são duas coisas, e o relatório as separa de propósito. Por isso a sequência de diagnóstico é sempre: status da execução → recibos da entrega → canal.
Os dois canais, e o que cada um exige
| Canal | provedor transacional | Cloud API oficial da Meta (número + conta comercial do produto) |
| Pré-requisito | chave do provedor + remetente + domínio verificado (DKIM) | credenciais da Cloud API, template aprovado e consentimento do destinatário |
| Sem o pré-requisito | envio vira no-op ou failed | a mensagem não sai |
| Fallback | — | gateway não-oficial, mantido apenas como fallback deprecated |
| Estado | Preview · entrega real NÃO MEDIDO | Preview · entrega real NÃO MEDIDO |
Gates humanos desta aula
O recibo real de e-mail é gate humano: o domínio de envio transacional ainda está em verificação, então em treino você vai ver o recibo e o estado, não a prova de chegada. O envio real por WhatsApp é gate humano sob allowlist. Em ambiente não produtivo, o efeito externo é barrado de propósito — e isso aparece como recusa explícita, nunca como sucesso fingido.
Exemplo Aurora
O "Semanal Aurora" roda segunda às 8h com dois destinatários: maria@example.com (e-mail) e um número consentido (WhatsApp). O histórico mostra Status: Sucesso · Entregas: 1 ok · 1 falha(s). maria abre os recibos: o do e-mail está sent com id do provedor; o do WhatsApp está failed sem próxima tentativa, com o motivo "canal não configurado". Conclusão correta: não é instabilidade, é credencial/template ausente — reprocessar não resolveria. Na semana seguinte, um 5xx do provedor de e-mail deixou o recibo failed com próxima tentativa; na terceira execução de envio ele foi para dlq, e aí sim virou investigação.
Erros comuns
| Erro | Por que é erro |
|---|---|
Ler sent como "chegou" | sent é "o canal aceitou"; só delivered significa entrega confirmada |
Reprocessar um failed sem próxima tentativa | esse estado é canal não configurado — retry não conserta ambiente |
Achar que execução Sucesso garante entrega | são dois eixos: o run executou, a entrega pode ter falhado |
| Mandar WhatsApp sem template aprovado ou sem consentimento | é o desenho de conformidade do canal oficial, não um bug |
| Reenviar o mesmo relatório esperando dois recibos | a idempotência devolve o recibo existente |
| Esperar prova de chegada em treino | entrega real é NÃO MEDIDO e depende de gate humano |
Faça você mesmo#
No workspace de treino Aurora Varejo (abra /relatorios no console):
- Abra o "Semanal Aurora" e adicione um destinatário de WhatsApp (um número de teste com DDD) além do e-mail.
- Execute com
Executar agorae espere a linha nova no Histórico de execuções. - Leia a coluna Entregas e anote quantos recibos saíram
oke quantos falharam. - Para cada recibo com falha, identifique se ele tem próxima tentativa — e portanto se é transiente (vale re-tentar) ou de configuração (não vale).
- Reprocesse a entrega que tem próxima tentativa e acompanhe o estado mudar; confirme que o número de tentativas sobe e que o teto é 3.
- Reenvie o mesmo relatório para o mesmo destino e confirme que não nasce um segundo recibo.
- Anote qual dos dois canais, no seu ambiente, está em gate humano — e por quê.
Atenção: este exercício depende de gate — em ambiente de treino o envio externo é barrado e os recibos refletem isso. O objetivo é ler os estados corretamente, não provar chegada.
Você terminou quando você conseguir apontar, para cada recibo da última execução, o estado, o número de tentativas e se ele admite reprocesso.
Checagem rápida#
Três perguntas no final da aula, corrigidas no servidor. A aula só conta como concluída depois da checagem.
Documentação relacionada#
Capacidades ensinadas#
C08.4 · C08.6 — o selo exibido na aula é sempre o estado mais conservador entre as capacidades citadas; nada aqui é "GA".