O Core possui um endpoint de bounce do Mandrill funcional — POST /webhooks/mandrill em WebhooksController — que processa eventos de hard_bounce e soft_bounce, aplica hard_bounce_score / soft_bounce_score e desativa endereços que ultrapassam o bounce_score_threshold. A correspondência depende do cabeçalho message_id do X-MC-Metadata, que o Email::Sender adiciona quando DISCOURSE_SMTP_ADDRESS é exatamente smtp.mandrillapp.com.
Eu estava tentando configurar isso, mas encontrei alguns problemas que achei que valesse a pena compartilhar, caso ajude outras pessoas. Sei que é um caso de uso de nicho, já que o Mandrill deixou de oferecer apenas SMTP há mais de uma década e só agora fornece o serviço de SMTP como um complemento ao produto de e-mails transacionais do Mailchimp. No entanto, um dos meus clientes tinha exatamente essa configuração, e por isso estamos aqui!
1. Não está documentado em lugar nenhum que eu consiga encontrar. O tópico sobre VERP não menciona isso, o que é razoável, porque o VERP não é a forma como o Mandrill funciona: os próprios documentos do Mailchimp afirmam que eles lidam com os bounces por conta própria, mesmo quando você configura um domínio Return-Path personalizado, portanto, bounces por mensagem nunca chegam a um receptor de e-mails auto-hospedado. Os webhooks do provedor são o único caminho, e não há nenhuma indicação óbvia para eles para quem começa pelo tópico de VERP.
2. O webhook não pode, na verdade, ser criado. WebhooksController#mandrill retorna 406 sempre que mandrill_authentication_key estiver vazio. Esse comportamento é intencional — é a correção para a CVE-2026-26077 (Discourse 2025.12.2 / 2026.1.1 / 2026.2.0), que fechou a forjagem não autenticada de payloads de bounce nos endpoints do SendGrid, Mailjet, Mandrill, Postmark, SparkPost e Mailpace. Portanto, esta não é uma solicitação para afrouxar a segurança; o problema é que a correção deixou o Mandrill sem uma forma de inicialização.
O Mandrill valida um novo webhook com um POST (e não com o HEAD para o qual mandrill_head foi escrito), vê o 406 e se recusa a salvar o webhook — portanto, a chave de que você precisa nunca fica visível. O caminho da API falha da mesma forma:
{"status":"error","code":-98,"name":"ValidationError","message":"Unable to validate webhook URL"}
Log de origem para uma tentativa pela interface do usuário:
"POST /webhooks/mandrill HTTP/2.0" "Mandrill-Webhook/1.0" 406
Os outros provedores possuem tokens de nível de conta, portanto, um administrador pode definir um segredo arbitrário antes de criar o webhook. A chave do Mandrill é emitida por webhook, apenas após o webhook ser salvo — portanto, é um dilema (Catch-22).
Tenho certeza de que o fluxo poderia ser corrigido, talvez respondendo com 200 à primeira verificação (que não tem payload de mandrill_events), permitindo a criação no Mandrill, depois o segredo pode ser copiado manualmente para o Discourse, e pronto. Ficarei feliz em abrir um PR se isso for aceitável.
3. Solução alternativa, caso alguém precise disso hoje. Crie o webhook apontando para uma URL que já retorne 200, sem gatilhos (payloads de bounce contêm endereços de destinatários, portanto, nunca aponte um webhook com gatilhos para um placeholder), depois instale a chave no Discourse e, uma vez que isso esteja feito, mude a URL do webhook:
# 1. crie contra um placeholder, sem eventos
curl -sS -X POST https://mandrillapp.com/api/1.0/webhooks/add.json \
-H 'Content-Type: application/json' \
-d '{"key":"<api-key>","url":"https://httpbin.org/status/200","description":"Discourse bounce handling","events":[]}'
# 2. coloque a auth_key retornada na configuração do site mandrill_authentication_key
# 3. redirecione para o fórum e ative os gatilhos - isso agora passa na validação
curl -sS -X POST https://mandrillapp.com/api/1.0/webhooks/update.json \
-H 'Content-Type: application/json' \
-d '{"key":"<api-key>","id":<id>,"url":"https://<forum>/webhooks/mandrill","description":"Discourse bounce handling","events":["hard_bounce","soft_bounce"]}'
Testado no 2026.9.0-latest.