Testar a entrega de e-mails (sandbox de e-mail)
O sandbox de e-mail testa handlers de webhook, lógica de supressão e resultados de entrega sem enviar para uma caixa de entrada real. Envie pelo API normal para um endereço em messagebird.dev. A parte local determina o resultado: bounce@messagebird.dev gera um bounce e delivered@messagebird.dev é entregue.
Um envio no sandbox usa os caminhos normais de aceitação, eventos e webhooks. Ele retorna a mesma resposta 202 e produz os mesmos formatos de payload de evento por destinatário que um envio em produção. O payload não tem nenhuma flag de teste. A mensagem não chega à infraestrutura externa de entrega nem a uma caixa de entrada real. Bounces e reclamações simulados não afetam a reputação de envio nem gravam na lista de supressão, então você pode reutilizar os endereços.
O sandbox não exige configuração: não há chave de ativação, modo de teste nem chave API especial. Ele é acionado apenas pelo endereço do destinatário, nos endpoints de envio normais (POST /v1/email/messages e POST /v1/email/batches) e em um broadcast. Um contato do público cujo endereço é um endereço de sandbox é simulado em vez de receber o envio. É assim que você ensaia uma campanha sem enviar e-mail para ninguém. Um destinatário simulado ainda conta na sua cota de envio, então o ensaio consome a mesma cota que o envio real consumiria.
Endereços mágicos
Todos os endereços ficam em @messagebird.dev. A parte local seleciona o resultado:
| Endereço | Resultado simulado | Sequência de webhooks | Observações |
|---|---|---|---|
delivered@ | O servidor de e-mail destinatário aceita a mensagem | email.accepted → email.processed → email.delivered | O caminho de sucesso |
bounce@ / hardbounce@ | Hard bounce: SMTP 550, 5.1.1 Unknown User, classe de bounce 10 | email.accepted → email.processed → email.bounced com bounce_type: "hard" | Nenhuma gravação na lista de supressão, então o endereço continua reutilizável |
softbounce@ | Soft bounce: SMTP 451, 4.3.0 Temporary failure, please retry, classe 20 | email.accepted → email.processed → email.bounced com bounce_type: "soft" | Soft bounces nunca geram supressão, sejam reais ou simulados |
deferred@ / delay@ | Adiamento: SMTP 451, 4.2.1 Mailbox temporarily unavailable, will retry, classe 21 | email.accepted → email.processed → email.deferred | O adiamento simulado é final: nenhuma nova tentativa acontece, então o destinatário permanece deferred |
complaint@ / spam@ | O destinatário denuncia a mensagem como spam (feedback_type: "abuse") | email.accepted → email.processed → email.complained | Nenhum registro de supressão; reutilizável |
suppressed@ | O destinatário é tratado como se já estivesse na sua lista de supressão | email.accepted → email.rejected com rejection_reason: "recipient_suppressed" | O processamento é interrompido exatamente como com um destinatário suprimido real: nenhum email.processed, nenhum evento de entrega |
reject@ | A mensagem é rejeitada antes de qualquer tentativa de entrega | email.accepted → email.rejected com rejection_reason: "transmission_failed" | Nenhum email.processed nem evento de entrega vem em seguida |
As sequências acima são os webhooks que um único envio produz. Em uma transmissão, desconsidere o email.accepted inicial: um destinatário de transmissão é aceito individualmente, mas registramos esse evento sem enviar um webhook para ele. Assim, cada sequência começa no evento seguinte, email.processed nos caminhos de entrega e email.rejected para suppressed@ e reject@. Todo o restante é idêntico, e a referência de eventos descreve a regra por completo.
Um envio pode misturar destinatários do sandbox e destinatários reais. Cada destinatário tem seu próprio ciclo de vida: os reais recebem a mensagem normalmente, e os do sandbox são simulados.
Os mesmos eventos também aparecem na linha do tempo da mensagem no log de e-mails e na API de eventos. Assim, você pode usar o sandbox sem um endpoint de webhook e consultar os resultados depois.
Regras de endereçamento
- A detecção considera apenas a parte local, e somente no domínio
messagebird.dev.bounce@yourdomain.comé um endereço normal. - Somente as partes locais da tabela de endereços mágicos são mágicas. Qualquer outro endereço em
messagebird.devé um destinatário normal. Quando você envia pelo domínio compartilhado de onboarding, esse endereço precisa pertencer a um membro verificado do espaço de trabalho. - A correspondência não diferencia maiúsculas de minúsculas:
Bounce@messagebird.devebounce@messagebird.devse comportam da mesma forma. - O subendereçamento com
+labelé removido antes da correspondência:bounce+signup-flow@messagebird.devcontinua gerando bounce. Use rótulos para correlacionar casos de teste. O endereço completo, com o rótulo, aparece nos seus eventos e webhooks, então cada execução de teste pode marcar seus próprios destinatários.
Passo a passo: simule um bounce do início ao fim
Você não precisa de um domínio de envio verificado. Envie a partir de onboarding@messagebird.dev, conforme descrito em Envie seu primeiro email. Os endereços de sandbox reconhecidos ficam isentos da restrição do domínio de onboarding que exige membros verificados, mas ainda contam para a cota diária desse domínio.
Confirme que você tem um endpoint de webhook inscrito em eventos de email (consulte Webhooks) e então envie:
const msg = await bird.email.send({
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["bounce+signup-flow@messagebird.dev"],
subject: "Sandbox bounce test",
html: "<p>This message will hard-bounce.</p>",
tags: [{ name: "flow", value: "signup" }],
metadata: { test_run: "docs-capture-1" },
});
console.log(msg.id, msg.status); // "em_…", "accepted"msg = client.email.send(
from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
to=["bounce+signup-flow@messagebird.dev"],
subject="Sandbox bounce test",
html="<p>This message will hard-bounce.</p>",
tags=[{"name": "flow", "value": "signup"}],
metadata={"test_run": "docs-capture-1"},
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"bounce+signup-flow@messagebird.dev"},
Subject: "Sandbox bounce test",
HTML: "<p>This message will hard-bounce.</p>",
Tags: []bird.Tag{{Name: "flow", Value: "signup"}},
Metadata: map[string]any{"test_run": "docs-capture-1"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$message = $bird->email->send(
from: 'Bird <onboarding@messagebird.dev>',
to: ['bounce+signup-flow@messagebird.dev'],
subject: 'Sandbox bounce test',
html: '<p>This message will hard-bounce.</p>',
);
echo $message->getId(), ' ', $message->getStatus();bird email send \
--from onboarding@messagebird.dev \
--html '<p>This message will hard-bounce.</p>' \
--metadata '{"test_run":"docs-capture-1"}' \
--subject 'Sandbox bounce test' \
--tag flow=signup \
--to bounce+signup-flow@messagebird.devcurl -X POST "https://us1.platform.bird.com/v1/email/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "onboarding@messagebird.dev",
"to": ["bounce+signup-flow@messagebird.dev"],
"subject": "Sandbox bounce test",
"html": "<p>This message will hard-bounce.</p>",
"tags": [{ "name": "flow", "value": "signup" }],
"metadata": { "test_run": "docs-capture-1" }
}'Se a sua chave começar com bk_eu1_, chame https://eu1.platform.bird.com em vez disso.
O API responde 202 Accepted com um ID de mensagem em_*, idêntico ao de um envio em produção. Assim, o caminho de código que você testa é o seu caminho real. Em seguida, seu endpoint de webhook recebe email.accepted, email.processed e, por fim, email.bounced. Todo evento repete o tags e o metadata do envio (null quando o envio não os tinha), e o payload de email.bounced traz a classificação completa do bounce:
{
"type": "email.bounced",
"timestamp": "2026-07-23T14:51:00.362Z",
"data": {
"email_id": "em_01ky7qanhrejer0bn34v38hrxh",
"recipient_id": "er_01ky7qanhrejds4decpk83q5qq",
"workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf",
"recipient": "bounce+signup-flow@messagebird.dev",
"recipient_role": "to",
"bounce_type": "hard",
"bounce_class": 10,
"bounce_code": "550",
"bounce_description": "5.1.1 Unknown User",
"sending_ip": null,
"tags": [{ "name": "flow", "value": "signup" }],
"metadata": { "test_run": "docs-capture-1" },
"broadcast_id": null
}
}O significado dos campos está na referência de eventos. Nenhum indicador de simulação aparece no payload: o tipo e o formato do evento são exatamente os de um hard bounce real. O único elemento que identifica a simulação é o próprio endereço do destinatário. Se o seu handler precisar tratar o tráfego de teste de forma especial, use o domínio de destinatário messagebird.dev como critério.
Para verificar que um destinatário já suprimido nunca recebe envios, repita o envio com suppressed@messagebird.dev. Confirme que você recebe email.accepted e depois email.rejected com rejection_reason: "recipient_suppressed". Você não deve receber email.processed nem eventos de entrega. Isso reproduz exatamente um destinatário suprimido real: a mensagem é aceita e depois interrompida durante o processamento, antes de qualquer envio.
O que o sandbox faz e o que não faz
- Nenhuma gravação na lista de supressão. Hard bounces e reclamações simulados não adicionam o destinatário à sua lista de supressão; é isso que mantém os endereços reutilizáveis. Seu webhook continua sendo disparado (
email.bounced,email.complained), então sua própria lógica de supressão é testada por completo. Para testar o caminho de rejeição de destinatários já suprimidos, use o endereço dedicadosuppressed@. - Nenhuma entrega real, em nenhum caso. Os destinatários de sandbox são interceptados antes que a mensagem chegue à infraestrutura de entrega. Nada é transmitido, nenhuma caixa de entrada é envolvida e sua reputação de envio não é afetada.
- A validação da solicitação continua valendo. Um envio de sandbox usa os endpoints normais, então as verificações de schema, os limites de tamanho e as regras de cabeçalho rejeitam uma solicitação inválida como de costume. O sandbox ignora tudo o que vem depois da entrega à infraestrutura: a renderização e o comportamento de entrega a partir desse ponto não são testados.
- Aberturas e cliques não são simulados. Um endereço mágico simula o resultado da transmissão, e ninguém abre a mensagem, então
email.openedeemail.clickedvêm apenas de emails reais. - As estatísticas incluem o tráfego do sandbox. Os envios para o sandbox contam nas estatísticas agregadas do seu espaço de trabalho e nas taxas de bounce e de reclamação. Muitos bounces no sandbox distorcem seus dashboards, mas não alteram sua reputação.
Próximos passos
- Eventos: o vocabulário completo de eventos e o ciclo de vida por destinatário
- Webhooks: inscrição, verificação de assinatura e novas tentativas
- Envie seu primeiro e-mail: o guia rápido do domínio de onboarding em que este passo a passo se baseia
- Supressões: como funciona a lista de supressão real
- Testar emails sem incomodar ninguém: um vídeo que apresenta os endereços do sandbox e os eventos que cada um gera
Recursos relacionados
Continue com a documentação, guias e exemplos sobre este tópico.