Skip to main content
Tudo que é enviado a um usuário seu — um link de verificação, um recibo, um win-back, uma nota de versão — passa por um único funil, e toda mensagem declara duas coisas ali. Parecem um campo só e não são.

Duas perguntas, nunca uma

Nenhuma é derivável da outra, e é por isso que as duas são declaradas. Um recibo é cortesia e não é marketing: ninguém se descadastra da resposta a algo que fez com o próprio endereço. Uma campanha é marketing e não é de graça: custa um envio como qualquer outra coisa.

Três categorias, e transacional não é uma delas

product_news é separada de marketing de propósito. Quem não quer ofertas ainda pode querer saber que as ferramentas mudaram, e um interruptor só para as duas perde essa pessoa inteira. surveys é separada porque o custo para quem recebe é diferente em natureza — um anúncio se lê, um NPS dá trabalho. Não existe categoria transactional, e nunca vai existir. Um link de verificação, uma redefinição de senha, um recibo, uma resposta do suporte e um aviso de cobrança continuam chegando depois que alguém desliga as três. Isso não é uma regra aplicada na hora do envio: o valor não existe, então não há linha que uma tela de preferências, uma atualização em massa ou um erro pudessem escrever para parar uma dessas.

A classe ordena mensagens, nunca momentos

Autenticação gratuita só é gratuita se o e-mail por trás dela for, então cada ambiente tem uma franquia mensal. Estourá-la nunca pode bloquear um login — e uma mensagem que não sai é um cadastro que não se completa, então “nunca bloquear” também não pode significar “nunca reter”. A saída é ordenar as mensagens: Você não escolhe isso. É o que a mensagem é, e campanhas são courtesy — uma campanha retida por orçamento é uma campanha, enquanto um magic link retido por orçamento é uma autenticação recusada com um passo a mais na frente.

O consentimento é honrado no envio, em uma instrução só

O registro da entrega e a checagem do consentimento são a mesma escrita. Uma mensagem que a pessoa desligou nunca é composta, em vez de ser composta e filtrada depois, e dois envios concorrendo no mesmo contato não conseguem os dois ler “não se descadastrou” e os dois sair. Numa campanha a recusa fica visível: o envio é registrado como suppressed com o motivo, que é o que permite ao suporte responder por que ela não recebeu com um fato. Um descadastro que chega entre a reserva do envio e a montagem da mensagem ainda para o envio — o funil confere de novo. Toda mensagem não transacional leva o link no rodapé e o par de cabeçalhos List-Unsubscribe / List-Unsubscribe-Post no envelope. Nenhuma mensagem transacional leva qualquer um dos dois: um recibo oferecendo parar de mandar recibos é uma oferta que este produto não faz. O link cai numa página hospedada que nomeia o seu produto, porque o tenant viaja no link ao lado do token. Atrás dela:
O GET é uma leitura e o POST é o ato. Gateways de e-mail e scanners de segurança buscam todo link de uma mensagem antes de uma pessoa ver, então um GET que descadastrasse descadastraria quem nunca clicou. Nenhum dos dois pede credencial de espécie alguma — o token uk_ns_… no caminho é tudo. Isso é o requisito e não uma lacuna: quem lê clicou de uma caixa de entrada num aparelho onde não está logado, e o descadastro de um clique da RFC 8058 é feito pelo cliente de e-mail, sem navegador nenhum. Um descadastro que primeiro pede para a pessoa lembrar a senha é um descadastro que falha, e o passo seguinte dela é o botão de spam. Pela mesma razão o token nunca expira e nunca é gasto. O cliente de e-mail postando em nome de quem lê e depois a própria pessoa clicando no mesmo link são os dois chamadores que de fato acontecem, e os dois têm que terminar no mesmo lugar. Um link desliga uma categoria de um contato, e é reversível na mesma página — {"opted_out": false} liga de volta. Ele nunca revela o endereço para o qual foi enviado: um token vindo de um e-mail encaminhado não pode virar uma consulta de quem é o dono daquela caixa.

A tela da própria pessoa

A resposta é sempre o catálogo inteiro, não as decisões que por acaso estão gravadas: a ausência de uma decisão gravada é o consentimento, então uma tela que renderizasse só o que existe seria uma página de preferências vazia para todo mundo que nunca a abriu. E-mail transacional não aparece na lista, porque não há nada aqui que pudesse pará-lo. Uma categoria que esta API não conhece é recusada pelo nome, transactional incluída. Uma decisão de consentimento descartada em silêncio é uma tela que diz salvo sobre uma preferência que nunca foi gravada, e a pessoa descobre pelo próximo e-mail. Esta é a única parte da autoadministração do contato que não exige sessão verificada. O pior que uma sessão não provada faz aqui é desligar uma categoria do contato que ela nomeou — exatamente o que o token na caixa de entrada daquele contato já faz sem sessão nenhuma. Exigir mais prova no controle dentro do produto do que no do e-mail seria o contrário do certo. Um descadastro publica um fato internamente e deliberadamente não é entregue como webhook. É o único tipo retido cuja razão é sobre direção e não sobre permissão: o que qualquer um construiria sobre “esta pessoa se descadastrou” é uma mensagem do próprio sistema, que é justamente a mensagem que o fato diz para não mandar.

O que esta plataforma mandou para uma pessoa

Uma leitura ordenada sobre as três coisas que enviam — campanhas, notificações no produto e e-mail, transacional incluído — então uma redefinição de senha fica ao lado do marketing. É protegida por customers:read e não por messaging:manage: compor uma campanha e responder o e-mail de reset chegou? são trabalhos diferentes, e o segundo é de quem já pode ver a pessoa. Leia tracked antes dos quatro carimbos de tempo. Quando é false, nada ia reportar sobre aquela mensagem — não há webhook de entrega configurado para o domínio remetente — então delivered_at, opened_at, clicked_at e bounced_at são null porque ninguém mediu, e não porque a mensagem falhou. Renderize isso como não rastreado. Quando é true, null quer dizer ainda não.

Permissões

messaging:manage — owner e admin — cobre templates e campanhas, leituras incluídas: uma mudança de preço ainda não enviada não é algo que uma permissão mais ampla deva poder ler. O log de entregas acima é customers:read, e nada aqui é limitado por plano exceto armar uma campanha.