rootmail

Core concepts

Sandbox & test recipients

Two lanes: rehearse your integration for free, then prove real delivery safely.

Testing email splits cleanly in two. The sandbox proves your integration — that you're calling the API correctly and your templates render. Test recipients prove the outside world — that mail actually goes out, gets signed, delivers, bounces and comes back through your webhooks.

The sandbox: a free rehearsal

A rm_test_… key runs every request through the full pipeline — validation, suppression, rendering, events — but never delivers and never bills. It's a separate workspace with its own data, so nothing you do there can touch production. Open it in the dashboard from Developers → Testing — it's deliberately kept out of the workspace picker, since it isn't a product you run.

  • Sandbox messages are stored with their full rendered content — read them back over the API, or in the dashboard under Testing.
  • Webhooks still fire for sandbox sends, so you can verify your handlers.
  • Sandbox sends are free forever and don't count toward any quota.
CarefulWhat the sandbox can't prove: delivery. Nothing is handed to a provider, so a green sandbox run says your code is right — not that your mail arrives.

Test recipients: the real path, safely

Every address at test.rootmail.dev is a scenario. Mail sent to one takes the real send path — your DKIM key, your sending provider, your webhooks — but is delivered to the provider's mailbox simulator. No person receives it, and it's excluded from your sender reputation, so you can force a hard bounce as often as you like at no cost.

Scenarios

delivered@test.rootmail.devdeliveredA clean delivery, with a real message.delivered event.
bounced@test.rootmail.devbouncedA permanent rejection. The address is auto-suppressed, exactly as a real hard bounce would be.
complained@test.rootmail.devcomplainedThe recipient reports spam — proves complaint handling and auto-suppression.
suppressed@test.rootmail.devsuppressedAlready on the provider's suppression list; the send is refused before it goes anywhere.
away@test.rootmail.devdeliveredDelivers, then returns an out-of-office auto-reply — useful for reply handling.
test-recipients.ts
// Prove your bounce handling, end to end.
const { data } = await mail.testing.list();
const bounce = data.find((t) => t.slug === "bounced")!;

await mail.messages.create({
  to: bounce.email,             // bounced@test.rootmail.dev
  subject: "Bounce me",
  html: "<p>This should never arrive.</p>",
});
// → message.sent, then message.bounced on your webhook,
//   and the address lands on your suppression list.

// Bounce tests suppress the address. Clear them to run again:
await mail.testing.reset();
GET/v1/test-recipients
Response
{
  "object": "list",
  "domain": "test.rootmail.dev",
  "data": [
    {
      "object": "test_recipient",
      "slug": "bounced",
      "email": "bounced@test.rootmail.dev",
      "label": "Hard bounce",
      "description": "The address rejects permanently…",
      "outcome": "bounced"
    }
  ]
}
POST/v1/test-recipients/reset
Response
{
  "object": "test_recipients_reset",
  "cleared": 2,
  "emails": ["bounced@test.rootmail.dev", "complained@test.rootmail.dev"]
}
noteTest recipients work from a live workspace (an ordinary send against your quota) and from the sandbox, where they still go out for real — free, up to 50 a day.
tipPoint your CI's ROOTMAIL_API_KEY at a test key, and assert on real responses without emailing anyone. Reach for a test recipient when the thing under test is delivery itself.
Sandbox & test recipients · rootmail developers