Tester la livraison d'e-mails (sandbox mail)
Le sandbox mail teste les gestionnaires de webhooks, la logique de suppression et les résultats de livraison sans envoyer vers une vraie boîte de réception. Envoyez via l'API normal à une adresse sur messagebird.dev. La partie locale détermine le résultat : bounce@messagebird.dev rebondit et delivered@messagebird.dev est livré.
Un envoi via le sandbox utilise les chemins normaux d'acceptation, d'événements et de webhooks. Il renvoie la même réponse 202 et produit les mêmes structures de payload d'événement par destinataire qu'un envoi en production. Le payload ne contient aucun indicateur de test. Le message n'atteint ni l'infrastructure de livraison externe ni une vraie boîte de réception. Les rebonds et plaintes simulés n'affectent pas la réputation d'envoi et n'écrivent pas dans la liste de suppression, vous pouvez donc réutiliser les adresses.
Le sandbox ne nécessite aucune configuration : pas de bascule, pas de mode test, pas de clé API spéciale. Il se déclenche uniquement sur l'adresse du destinataire, sur les endpoints d'envoi normaux (POST /v1/email/messages et POST /v1/email/batches) et sur un broadcast : un contact dans l'audience dont l'adresse est une adresse sandbox est simulé au lieu de recevoir l'envoi, ce qui vous permet de répéter une campagne sans envoyer de courrier. Un destinataire simulé est néanmoins décompté de votre quota d'envoi, la répétition consomme donc le même quota que l'envoi réel.
Adresses magiques
Toutes les adresses sont sur @messagebird.dev. La partie locale sélectionne le résultat :
| Adresse | Résultat simulé | Séquence de webhooks | Notes |
|---|---|---|---|
| delivered@ | Le serveur de messagerie récepteur accepte le message | email.accepted → email.processed → email.delivered | Le chemin nominal |
| bounce@ / hardbounce@ | Rebond dur : SMTP 550, 5.1.1 Unknown User, bounce class 10 | email.accepted → email.processed → email.bounced avec bounce_type: "hard" | Pas d'écriture dans la liste de suppression, l'adresse reste réutilisable |
| softbounce@ | Rebond temporaire : SMTP 451, 4.3.0 Temporary failure, please retry, class 20 | email.accepted → email.processed → email.bounced avec bounce_type: "soft" | Les rebonds temporaires ne déclenchent jamais de suppression, réels ou simulés |
| deferred@ / delay@ | Report : SMTP 451, 4.2.1 Mailbox temporarily unavailable, will retry, class 21 | email.accepted → email.processed → email.deferred | Le report simulé est terminal : aucune nouvelle tentative ne suit, le destinataire reste deferred |
| complaint@ / spam@ | Le destinataire signale le message comme spam (feedback_type: "abuse") | email.accepted → email.processed → email.complained | Pas d'écriture de suppression ; réutilisable |
| suppressed@ | Le destinataire est traité comme déjà présent dans votre liste de suppression | email.accepted → email.rejected avec rejection_reason: "recipient_suppressed" | Court-circuite le traitement exactement comme un vrai destinataire supprimé : pas de email.processed, pas d'événements de livraison |
| reject@ | Le message est rejeté avant toute tentative de livraison | email.accepted → email.rejected avec rejection_reason: "transmission_failed" | Aucun email.processed ni événement de livraison ne suit |
Les séquences ci-dessus sont les webhooks produits par un envoi unitaire. Pour un broadcast, retirez le email.accepted initial : un destinataire de broadcast est accepté de plein droit, mais nous enregistrons cet événement sans envoyer de webhook pour lui, chaque séquence commence donc à ce qui suit, email.processed sur les chemins de livraison et email.rejected pour suppressed@ et reject@. Tout le reste est identique, et la référence des événements couvre la règle en détail.
Un envoi peut mélanger des destinataires sandbox et réels. Chaque destinataire suit son propre cycle de vie : les destinataires réels sont livrés normalement, les destinataires sandbox sont simulés.
Les mêmes événements apparaissent aussi sur la chronologie du message dans le journal des e-mails et dans l'API des événements, vous pouvez donc utiliser le sandbox sans endpoint webhook et consulter les résultats directement.
Règles d'adressage
- La détection se fait uniquement sur la partie locale, et uniquement sur le domaine messagebird.dev. bounce@yourdomain.com est une adresse normale.
- Seules les parties locales du tableau des adresses magiques sont magiques. Toute autre adresse sur messagebird.dev est un destinataire normal. Lorsque vous envoyez depuis le domaine d'intégration partagé, cette adresse doit appartenir à un membre vérifié de l'espace de travail.
- La correspondance est insensible à la casse : Bounce@messagebird.dev et bounce@messagebird.dev se comportent de manière identique.
- Le sous-adressage +label est retiré avant la correspondance : bounce+signup-flow@messagebird.dev rebondit toujours. Utilisez des labels pour corréler les cas de test ; l'adresse complète, label inclus, apparaît dans vos événements et webhooks, chaque série de tests peut donc identifier ses propres destinataires.
Guide pas à pas : simuler un rebond de bout en bout
Vous n'avez pas besoin d'un domaine d'envoi vérifié. Envoyez depuis onboarding@messagebird.dev, comme décrit dans Envoyer votre premier e-mail. Les adresses sandbox reconnues sont exemptées de la restriction de membre vérifié du domaine d'intégration, mais sont toujours décomptées de son quota journalier.
Assurez-vous d'avoir un endpoint webhook abonné aux événements e-mail (voir Webhooks), puis envoyez :
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.dev{
"name": "email_send",
"arguments": {
"from": {
"email": "onboarding@messagebird.dev"
},
"html": "<p>This message will hard-bounce.</p>",
"metadata": {
"test_run": "docs-capture-1"
},
"subject": "Sandbox bounce test",
"tags": [
{
"name": "flow",
"value": "signup"
}
],
"to": [
{
"email": "bounce+signup-flow@messagebird.dev"
}
]
}
}curl -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" }
}'Si votre clé commence par bk_eu1_, appelez https://eu1.platform.bird.com à la place.
L'API répond 202 Accepted avec un identifiant de message em_*, indiscernable d'un envoi en production. C'est précisément le but : le chemin de code que vous testez est votre chemin réel. Votre endpoint webhook reçoit ensuite email.accepted, email.processed, et enfin email.bounced. Chaque événement reprend le tags et le metadata de l'envoi (null si l'envoi n'en avait pas), et le payload email.bounced contient la classification complète du rebond :
Exemple de code
{
"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
}
}La signification des champs se trouve dans la référence des événements. Aucun indicateur de simulateur n'apparaît nulle part dans le payload : le type et la structure de l'événement sont exactement ceux que produit un vrai rebond dur. Le seul élément qui l'identifie comme simulé est l'adresse du destinataire elle-même ; si votre gestionnaire doit traiter le trafic de test différemment, basez-vous sur le domaine destinataire messagebird.dev.
Pour vérifier qu'un destinataire déjà supprimé ne reçoit jamais d'envoi, répétez l'envoi avec suppressed@messagebird.dev. Vérifiez que vous recevez email.accepted, puis email.rejected avec rejection_reason: "recipient_suppressed". Vous ne devez recevoir aucun email.processed ni événement de livraison. Ce comportement est identique à celui d'un vrai destinataire supprimé : le message est accepté, puis court-circuité pendant le traitement avant tout envoi.
Ce que le sandbox fait et ne fait pas
- Pas d'écriture dans la liste de suppression. Les rebonds durs et plaintes simulés n'ajoutent pas le destinataire à votre liste de suppression ; c'est ce qui permet de réutiliser les adresses. Votre webhook se déclenche quand même (email.bounced, email.complained), votre propre logique de suppression est donc pleinement exercée. Pour tester le chemin de rejet d'un destinataire déjà supprimé, utilisez l'adresse dédiée suppressed@.
- Aucune livraison réelle, jamais. Les destinataires sandbox sont interceptés avant que le message n'atteigne l'infrastructure de livraison. Rien n'est transmis, aucune boîte de réception n'est impliquée et votre réputation d'envoi reste intacte.
- La validation de la requête reste active. Un envoi sandbox utilise les endpoints normaux, les vérifications de schéma, les limites de taille et les règles d'en-tête rejettent donc une requête invalide comme d'habitude. Ce que le sandbox ignore, c'est tout ce qui suit le transfert : le rendu et le comportement de livraison au-delà de ce point ne sont pas exercés.
- Les ouvertures et les clics ne sont pas simulés. Une adresse magique simule le résultat de la transmission, et personne n'ouvre le message ; email.opened et email.clicked ne proviennent donc que de vrais e-mails.
- Les statistiques incluent le trafic sandbox. Les envois sandbox sont décomptés dans les statistiques agrégées de votre espace de travail et dans les taux de rebonds et de plaintes. Un volume élevé de rebonds sandbox fausse vos tableaux de bord tout en laissant votre réputation inchangée.
Étapes suivantes
- Événements : le vocabulaire complet des événements et le cycle de vie par destinataire
- Webhooks : abonnement, vérification de signature et nouvelles tentatives
- Envoyer votre premier e-mail : le guide de démarrage rapide sur le domaine d'intégration sur lequel ce pas à pas s'appuie
- Suppressions : fonctionnement de la liste de suppression réelle
- Tester l'email sans spammer qui que ce soit : une vidéo qui parcourt les adresses sandbox et les événements que chacune produit
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet. Les ressources sont en anglais.
Explorer la fonctionnalitéTest email deliverySuivre le parcours d'apprentissageOperate messaging reliably
Essayez la pratique et obtenez un guide d'implémentation