Documentation
Sign inGet started

Test email delivery (mail sandbox)

The mail sandbox lets you exercise your entire email integration — webhook handlers, suppression logic, retry behaviour — with real API calls that never deliver anywhere. Send to a magic address at messagebird.dev and the outcome is determined by the address itself: bounce@messagebird.dev always bounces, delivered@messagebird.dev always delivers, and so on. The full address list is also mirrored at messagebird.dev.
What makes this different from validation-only sandbox modes: a sandbox send runs through the real production pipeline. You get the same 202 response, the same per-recipient event sequence, and the same webhook deliveries — identical event types and payload shapes, with no "this is a test" flag — as a production send. The message is simply never handed to the delivery infrastructure, so it never reaches a real inbox. That means zero reputation risk: simulated bounces and complaints don't touch your sending reputation, and they don't write to your suppression list, so the same bounce address is reusable across every test run.
There's nothing to enable and no special API key. Sandbox behaviour is triggered purely by the recipient address on the normal send endpoints (POST /v1/email/messages and POST /v1/email/batches).

Magic addresses

All addresses are on @messagebird.dev. The local-part selects the outcome:
AddressSimulated outcomeEvents you'll receiveNotes
delivered@Accepted by the receiving ISP and deliveredemail.acceptedemail.processedemail.deliveredThe happy path
bounce@ / hardbounce@Hard bounce — SMTP 550 5.1.1 Unknown User, bounce class 10email.acceptedemail.processedemail.bouncedDoes not add the address to your suppression list, so it's reusable
softbounce@Transient soft bounce — SMTP 451, bounce class 20email.acceptedemail.processedemail.bounced (transient)No suppression write
deferred@ / delay@Transient deferral — SMTP 451, bounce class 21email.acceptedemail.processedemail.deferred
complaint@ / spam@Recipient marks the message as spam (feedback-loop complaint)email.acceptedemail.processedemail.complainedNo suppression write; reusable
suppressed@Recipient is already on the suppression listemail.rejected with rejection_reason: recipient_suppressed — nothing elseShort-circuits at processing exactly like a real suppressed recipient; no send is attempted
reject@The delivery provider rejects the transmission (4xx)email.acceptedemail.rejected with rejection_reason: transmission_failedNo delivery events follow the rejection
A message can mix sandbox and real recipients — each recipient gets its own lifecycle, real recipients are delivered normally, and sandbox recipients are simulated.

Addressing rules

  • Detection is by local-part only, and only on the messagebird.dev domain. bounce@yourdomain.com is just a normal address.
  • Case-insensitive: Bounce@messagebird.dev and bounce@messagebird.dev behave identically.
  • +label subaddressing is stripped before matching: bounce+signup-flow@messagebird.dev still bounces. Use labels to correlate test cases — the full address (label included) appears in your events and webhooks, so each test run can tag its own recipients.

Walkthrough: simulate a bounce, end to end

You don't need a verified sending domain — send from Bird's shared onboarding domain, onboarding@messagebird.dev, the same one used in Send your first email. Sandbox recipients are exempt from the onboarding domain's usual recipient restrictions (they do still count toward its daily send quota), so the whole test is self-contained on messagebird.dev: one Bird-owned domain on both ends.
Make sure you have a webhook endpoint subscribed to email events (see Webhooks), then send:
Esempio di codice
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>"
  }'
If your key starts with bk_eu1_, call https://eu1.platform.bird.com instead.
The API responds 202 Accepted with an em_* message ID — indistinguishable from a production send, which is the point: the code path you're testing is your real one. Your webhook endpoint then receives email.accepted, email.processed, and finally email.bounced, with a payload shaped like this (the bounce-detail fields in data are still being finalized — today the bounce classification is available on the message's event timeline via the API; the authoritative field reference is the events page):
Esempio di codice
{
  "type": "email.bounced",
  "timestamp": "2026-06-10T14:03:12Z",
  "data": {
    "email_id": "em_019c1930687b7bfa8a1b2c3d4e5f6789",
    "recipient_id": "er_01krdgeqcxet5s7t44vh8rt9mg",
    "workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
    "recipient": "bounce+signup-flow@messagebird.dev",
    "recipient_role": "to"
  }
}
Note there is no simulator flag anywhere in the payload — the event type and shape are exactly what a real hard bounce produces. The only thing that identifies it as simulated is the recipient address itself, so if your handler needs to special-case test traffic, key off the messagebird.dev recipient domain.
To verify the other half of bounce handling — that an already-suppressed recipient never gets sent to — repeat the send with suppressed@messagebird.dev and assert you receive a single email.rejected event with rejection_reason: recipient_suppressed and no delivery events.

What the sandbox does and doesn't do

  • No suppression-list writes. Simulated hard bounces and complaints do not add the recipient to your suppression list — that's what keeps the addresses reusable. Your webhook still fires (email.bounced, email.complained), so your own suppression logic is fully exercised; to test the already-suppressed rejection path, use the dedicated suppressed@ address.
  • No real delivery, ever. Sandbox recipients are intercepted before the message is handed to the delivery infrastructure. Nothing is transmitted, no inbox is involved, and your sending reputation is untouched.
  • No content validation. Because the message is never assembled for delivery, the sandbox exercises the event and webhook path, not rendering or content checks.
  • Opens and clicks aren't simulated yet. No recipient address can trigger an engagement event, so email.opened / email.clicked simulation is planned for a later phase. Today the sandbox covers delivery-pipeline outcomes only.
  • Stats currently include sandbox traffic. Sandbox sends count toward your workspace's aggregate stats and bounce/complaint rates for now; an exclusion filter is planned. Heavy sandbox bouncing will skew your dashboards (not your reputation).
  • Events — the full event vocabulary and per-recipient lifecycle
  • Webhooks — subscribing, signature verification, and retries
  • Send your first email — the onboarding-domain quickstart this walkthrough builds on
  • Suppressions — how the real suppression list works