Probar la entrega de email (sandbox de correo)
El sandbox de correo prueba los gestores de webhooks, la lógica de supresión y los resultados de entrega sin enviar a una bandeja de entrada real. Envía a través del API habitual a una dirección en messagebird.dev. La parte local determina el resultado: bounce@messagebird.dev rebota y delivered@messagebird.dev se entrega.
Un envío al sandbox usa los flujos habituales de aceptación, eventos y webhooks. Devuelve la misma respuesta 202 y produce las mismas estructuras de payload de evento por destinatario que un envío en producción. El payload no incluye ningún indicador de prueba. El mensaje no llega a la infraestructura de entrega externa ni a una bandeja de entrada real. Los rebotes y quejas simulados no afectan a la reputación de envío ni se escriben en la lista de supresión, así que puedes reutilizar las direcciones.
El sandbox no necesita configuración: ni interruptor, ni modo de prueba, ni clave API especial. Se activa únicamente por la dirección del destinatario, en los endpoints de envío habituales (POST /v1/email/messages y POST /v1/email/batches) y en un broadcast: si un contacto de la audiencia tiene una dirección del sandbox, se simula en lugar de enviarle el mensaje. Así ensayas una campaña sin escribir a nadie. Un destinatario simulado sigue contando para tu cuota de envío, de modo que el ensayo consume la misma cuota que el envío real.
Direcciones mágicas
Todas las direcciones están en @messagebird.dev. La parte local elige el resultado:
| Dirección | Resultado simulado | Secuencia de webhooks | Notas |
|---|---|---|---|
delivered@ | El servidor de correo receptor acepta el mensaje | email.accepted → email.processed → email.delivered | El caso de éxito |
bounce@ / hardbounce@ | Rebote permanente: SMTP 550, 5.1.1 Unknown User, clase de rebote 10 | email.accepted → email.processed → email.bounced con bounce_type: "hard" | No se escribe en la lista de supresión, así que puedes reutilizar la dirección |
softbounce@ | Rebote temporal: SMTP 451, 4.3.0 Temporary failure, please retry, clase 20 | email.accepted → email.processed → email.bounced con bounce_type: "soft" | Los rebotes temporales nunca provocan una supresión, ni reales ni simulados |
deferred@ / delay@ | Aplazamiento: SMTP 451, 4.2.1 Mailbox temporarily unavailable, will retry, clase 21 | email.accepted → email.processed → email.deferred | El aplazamiento simulado es definitivo: no se produce ningún reintento, así que el destinatario permanece en deferred |
complaint@ / spam@ | El destinatario marca el mensaje como spam (feedback_type: "abuse") | email.accepted → email.processed → email.complained | No se escribe en la lista de supresión; la dirección se puede reutilizar |
suppressed@ | El destinatario se trata como si ya estuviera en tu lista de supresión | email.accepted → email.rejected con rejection_reason: "recipient_suppressed" | El procesamiento se interrumpe exactamente igual que con un destinatario suprimido real: no hay email.processed ni eventos de entrega |
reject@ | El mensaje se rechaza antes de cualquier intento de entrega | email.accepted → email.rejected con rejection_reason: "transmission_failed" | No llega ningún email.processed ni eventos de entrega |
Las secuencias anteriores son los webhooks que produce un envío individual. En un broadcast, omite el email.accepted inicial: cada destinatario de un broadcast se acepta por separado, pero registramos ese evento sin enviar un webhook, así que cada secuencia empieza por el evento siguiente: email.processed en las rutas de entrega y email.rejected para suppressed@ y reject@. Todo lo demás es idéntico, y la referencia de eventos describe la regla completa.
Un envío puede combinar destinatarios del sandbox y destinatarios reales. Cada destinatario tiene su propio ciclo de vida: los reales reciben el correo con normalidad y los del sandbox se simulan.
Los mismos eventos aparecen también en la cronología del mensaje en el registro de correo y en los eventos API, así que puedes usar el sandbox sin un endpoint de webhook y consultar después los resultados.
Reglas de direccionamiento
- La detección se basa solo en la parte local y solo en el dominio
messagebird.dev.bounce@yourdomain.comes una dirección normal. - Solo las partes locales de la tabla de direcciones mágicas son mágicas. Cualquier otra dirección en
messagebird.deves un destinatario normal. Si envías desde el dominio compartido de onboarding, esa dirección debe pertenecer a un miembro verificado del espacio de trabajo. - La coincidencia no distingue mayúsculas de minúsculas:
Bounce@messagebird.devybounce@messagebird.devse comportan igual. - La subdirección
+labelse elimina antes de comparar:bounce+signup-flow@messagebird.devsigue rebotando. Usa etiquetas para correlacionar casos de prueba; la dirección completa, con la etiqueta, aparece en tus eventos y webhooks, así que cada ejecución de prueba puede marcar sus propios destinatarios.
Tutorial: simula un rebote de principio a fin
No necesitas un dominio de envío verificado. Envía desde onboarding@messagebird.dev, como se describe en Envía tu primer correo. Las direcciones del sandbox reconocidas están exentas de la restricción de miembros verificados del dominio de incorporación, pero siguen contando para su cuota diaria.
Asegúrate de tener un endpoint de webhook suscrito a los eventos de correo (consulta Webhooks) y, después, envía:
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" }
}'Si tu clave empieza por bk_eu1_, llama a https://eu1.platform.bird.com en su lugar.
API responde 202 Accepted con un ID de mensaje em_*, idéntico al de un envío de producción. Por eso la ruta de código que pruebas es la real. Después, tu endpoint de webhook recibe email.accepted, email.processed y, por último, email.bounced. Cada evento repite los valores tags y metadata del envío (null si el envío no los tenía), y la carga útil de email.bounced incluye la clasificación completa del rebote:
{
"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
}
}El significado de cada campo está en la referencia de eventos. La carga útil no contiene ningún indicador de simulación: el tipo y la estructura del evento son exactamente los de un rebote permanente real. Lo único que lo identifica como simulado es la propia dirección del destinatario, así que, si tu controlador debe tratar aparte el tráfico de prueba, básate en el dominio de destinatario messagebird.dev.
Para comprobar que nunca se envía nada a un destinatario ya suprimido, repite el envío con suppressed@messagebird.dev. Comprueba que recibes email.accepted y después email.rejected con rejection_reason: "recipient_suppressed". No deberías recibir email.processed ni eventos de entrega. Esto reproduce exactamente un destinatario suprimido real: el mensaje se acepta y luego se interrumpe durante el procesamiento, antes de cualquier envío.
Qué hace y qué no hace el sandbox
- No escribe en la lista de supresión. Los rebotes permanentes y las quejas simulados no añaden el destinatario a tu lista de supresión; por eso las direcciones se pueden reutilizar. Tu webhook se sigue activando (
email.bounced,email.complained), así que tu propia lógica de supresión se prueba por completo. Para probar el rechazo de destinatarios ya suprimidos, usa la dirección específicasuppressed@. - Nunca hay entrega real. Los destinatarios del sandbox se interceptan antes de que el mensaje llegue a la infraestructura de entrega. No se transmite nada, no interviene ninguna bandeja de entrada y tu reputación de envío no cambia.
- La validación de solicitudes se sigue aplicando. Un envío al sandbox usa los endpoints normales, así que las comprobaciones de esquema, los límites de tamaño y las reglas de encabezados rechazan una solicitud no válida como siempre. Lo que el sandbox omite es todo lo que ocurre después de la entrega del mensaje al sistema: no se prueban el renderizado ni el comportamiento de entrega a partir de ese punto.
- No se simulan aperturas ni clics. Una dirección mágica simula el resultado de la transmisión y nadie abre el mensaje, así que
email.openedyemail.clickedsolo proceden de correo real. - Las estadísticas incluyen el tráfico del sandbox. Los envíos al sandbox cuentan para las estadísticas agregadas de tu espacio de trabajo y para tus tasas de rebote y de quejas. Si generas muchos rebotes en el sandbox, tus paneles mostrarán datos distorsionados, aunque tu reputación no cambie.
Próximos pasos
- Eventos: el vocabulario completo de eventos y el ciclo de vida de cada destinatario
- Webhooks: suscripción, verificación de firmas y reintentos
- Envía tu primer correo: la guía rápida con el dominio de incorporación en la que se basa este recorrido
- Supresiones: cómo funciona la lista de supresión real
- Prueba emails sin enviar spam a nadie: un vídeo que recorre las direcciones del sandbox y los eventos que produce cada una
Recursos relacionados
Continúa con la documentación, guías y ejemplos de este tema.