Projects and routes

Sandbox mode

Use Sandbox mode to test your email content, delivery handling, and webhook integration without sending email to recipients.

Sandbox mode applies to a project. It is not a route type. Transactional and Broadcast routes keep their normal settings. Inbound routes continue to receive email as usual.

Sandbox messages do not go to recipient mail servers. Webhook deliveries are real HTTPS requests. They can start work in your application. Use a test endpoint, or check the sandbox field before your webhook handler starts a customer action.

What Sandbox mode changes

AreaSandbox behavior
Outbound deliveryLettermint records a simulated result and does not connect to a recipient mail server.
Sender domainsYou can use an unverified or unknown From domain. A verified team domain the project may use is linked to the message, the same as Live. A domain restricted from the project is rejected, the same as Live.
Message processingAuthentication, address, content, attachment, size, recipient, schedule, and idempotency checks still apply.
RoutesTransactional and Broadcast routes can send Sandbox messages. Inbound routes are not affected.
Events and webhooksLettermint creates message history and sends signed webhook requests with the normal event names.
Billing and statisticsSandbox messages do not use your email quota and do not affect billable usage, delivery statistics, reputation, warmup, or Live sending limits.
SuppressionsSandbox messages ignore Live suppression lists and do not add, change, or remove suppression records.
Project limitsA Sandbox project counts toward your project limit in the same way as a Live project.
RetentionLettermint keeps Sandbox messages, source, and events under the normal retention rules.

Lettermint still prepares the message source, plain-text part, attachments, preview, tracking links, and hosted unsubscribe links. A simulation does not fetch a customer link or send an automatic reply email.

The dashboard and API show delivery_mode and sandbox_result for each message. A project can contain both Live and Sandbox history after a mode change. You can filter the message list with filter[delivery_mode]=live or filter[delivery_mode]=sandbox.

Create a Sandbox project

To create a Sandbox project in the dashboard:

  1. Open Projects and select Create project.
  2. Select Sandbox as the delivery mode.
  3. Select Transactional, Broadcast, or Both as the email type.
  4. Enter the project name, select the SMTP setting, and create the project.

The Team API also accepts delivery_mode when you create a project:

JSONCode
{ "name": "CI email tests", "delivery_mode": "sandbox", "initial_routes": "both", "smtp_enabled": true }

The allowed values are live and sandbox. If you omit delivery_mode, Lettermint creates a Live project.

Change the delivery mode

Open the project, go to Settings, and use the Delivery mode switch. Select Save changes, then confirm the mode change.

The new mode applies only to messages that Lettermint accepts after the change.

Message stateResult after a mode change
Accepted in Live modeThe message stays Live and can still be delivered.
Accepted in Sandbox modeThe message stays Sandbox and remains simulated.
Scheduled in Live modeThe message uses Live delivery when its schedule releases it.
Scheduled in Sandbox modeThe message runs its saved simulation when its schedule releases it.

The same rule applies to queued messages, retries, and recovery. The message keeps the mode and result that Lettermint saved at acceptance.

You can also send delivery_mode in a Team API project update request:

JSONCode
{ "delivery_mode": "live" }

Your Project API tokens and SMTP credentials stay active when you change the mode. After you switch a project to Live, each new accepted message can go to its real recipients. Verify your sender domains and test configuration before you confirm the change.

Live messages must use a verified From domain. Sandbox messages can use an unverified From domain because no recipient mail server receives the message.

Select a result with the Sending API

Add sandbox_result to a request to POST /v1/send. The field works only when the saved project mode is Sandbox.

JSONCode
{ "from": "Build checks <ci@unverified.example>", "to": ["recipient@example.com"], "subject": "Test hard bounce handling", "text": "This message will not be delivered.", "sandbox_result": "hard_bounced" }

The accepted response of a Sandbox send includes sandbox: true and the message-level sandbox_result:

JSONCode
{ "message_id": "019b9d66-9ec8-73ba-a58f-b4f1f6fc5982", "status": "pending", "sandbox": true, "sandbox_result": "hard_bounced" }

The response of a Live send does not include sandbox or sandbox_result. The same applies to each item in a batch response.

If you omit sandbox_result, Lettermint uses delivered. Lettermint rejects sandbox_result when the project is Live.

Batch requests

Add sandbox_result to each item in a POST /v1/send/batch request. Use separate items when you need different results.

JSONCode
[ { "from": "Build checks <ci@unverified.example>", "to": ["delivery-case@example.com"], "subject": "Test delivery handling", "text": "Simulate delivery.", "sandbox_result": "delivered" }, { "from": "Build checks <ci@unverified.example>", "to": ["complaint-case@example.com"], "subject": "Test complaint handling", "text": "Simulate a complaint.", "sandbox_result": "spam_complaint" } ]

One sandbox_result applies to all To, Cc, and Bcc recipients in that message.

See the Sending API reference for the complete single-send and batch request schemas.

Select a result with SMTP

Add X-Lettermint-Sandbox-Result to an SMTP message:

Code
X-Lettermint-Sandbox-Result: soft_bounced

Omit the header to simulate delivered. The header supports the same values as the Sending API.

Lettermint rejects this header when the saved project mode is Live. It also rejects duplicate headers and unsupported values. Lettermint removes the control header from the stored customer headers.

See the SMTP guide for connection settings and other control headers.

Per-recipient results with test addresses

A recipient at a Lettermint testing domain can select its own simulated result, the same way Lettermint test addresses work in a Live project. The result comes from the local part before +, so hardbounce+signup@lettermint.dev simulates a hard bounce for that recipient. This overrides the message-level sandbox_result for that recipient only. Other recipients on the same message keep the message-level result.

This works for both lettermint.dev and the compatible testing.lettermint.co domain. It works over the Sending API and SMTP, and is case-insensitive. Lettermint keeps the +label in the recipient address in events and webhooks, for example hardbounce+signup@lettermint.dev.

Lettermint accepts these local parts:

Local partResult
okdelivered
softbouncesoft_bounced
hardbouncehard_bounced
spamcomplaintspam_complaint
dsnhard_bounced
Any sandbox_result value, with or without underscores (for example hard_bounced or hardbounced)That result

A recipient at a testing domain with a local part that does not match one of these is rejected when Lettermint accepts the message.

Precedence, from highest to lowest:

PrecedenceSource
1The recipient's test address
2The message-level sandbox_result
3delivered (default)

Example: API

JSONCode
{ "from": "Build checks <ci@unverified.example>", "to": ["hardbounce+signup@lettermint.dev", "recipient@example.com"], "subject": "Test per-recipient results", "text": "One recipient hard bounces. The other uses the message-level result.", "sandbox_result": "delivered" }

hardbounce+signup@lettermint.dev simulates hard_bounced. recipient@example.com simulates delivered, the message-level result.

Example: SMTP

Code
X-Lettermint-Sandbox-Result: delivered To: hardbounce+signup@lettermint.dev, recipient@example.com

Same result: the first recipient hard bounces, the second follows the message-level delivered result.

The dashboard and API also show sandbox_result on each message recipient, alongside the message-level delivery_mode and sandbox_result on the message itself.

Supported results

Every accepted Sandbox message starts with message.created. Lettermint then records the selected sequence for each recipient.

sandbox_resultEvent sequence
deliveredmessage.sent, then message.delivered
hard_bouncedmessage.sent, then message.hard_bounced
soft_bouncedmessage.sent, then message.soft_bounced
deferredmessage.sent, message.soft_bounced, then message.delivered after five seconds
failedmessage.sent, message.soft_bounced, then message.failed
suppressedmessage.suppressed
spam_complaintmessage.sent, message.delivered, then message.spam_complaint
auto_repliedmessage.sent, message.delivered, then message.auto_replied
openedmessage.sent, message.delivered, then message.opened
clickedmessage.sent, message.delivered, message.opened, then message.clicked
unsubscribedmessage.sent, message.delivered, then message.unsubscribed

Lettermint records each simulation step once. A retry does not create a second copy of a completed event.

The suppressed result creates message.suppressed for the current message only. It does not create a suppression record. Simulated bounces, complaints, and unsubscribes also do not change suppression lists.

If you open a hosted unsubscribe link for a Sandbox message, Lettermint returns the normal success response and records one Sandbox unsubscribe event. It does not add the recipient to a suppression list.

Test webhooks

Sandbox sends make real signed HTTP requests to webhooks that accept Sandbox events. Normal delivery retries and signature headers apply.

Outbound message payloads contain these top-level fields:

JSONCode
{ "id": "54d7e8c9-1195-4ba0-9d3f-b9af92305add", "event": "message.hard_bounced", "timestamp": "2026-09-21T12:00:00.000Z", "sandbox": true, "sandbox_result": "hard_bounced", "context": { "scope": "route", "team_id": "9f4e3d2c-1b0a-4987-9654-3210fedcba98", "project_id": "7d6c5b4a-3210-4987-9654-fedcba987654", "route_id": "1a2b3c4d-5678-4987-9654-abcdef123456" }, "data": { "message_id": "019b9d66-9ec8-73ba-a58f-b4f1f6fc5982", "recipient": "recipient@example.com" } }

Use sandbox for a simple branch in your webhook handler. Use sandbox_result when the handler must check the selected test case. For a Live message, sandbox is false and sandbox_result is null. On a recipient event, sandbox_result is that recipient's resolved result, which can differ from the message-level result when the recipient used a test address. On message.created, it is the message-level result.

Each webhook has a delivery mode filter:

FilterEvents sent to the webhook
LiveLive events only
SandboxSandbox events only
BothLive and Sandbox events

New team, project, and route webhooks default to Live. Existing webhooks keep their current filter. Changing a project mode does not change this filter. Disabled webhooks stay disabled.

The Test webhook action sends one webhook.test request. Use a Sandbox send when you need to test a complete message event sequence, the delivery mode filter, and the Sandbox payload fields.

Always verify the webhook signature. Store each webhook id that your application processes so a retry cannot run the same action twice.

Use Sandbox mode in CI

Use a separate Sandbox project for CI, staging, and local development. This keeps the mode stable and keeps production credentials out of test jobs.

A practical test has these steps:

  1. Send a message with the result that the test needs.
  2. Store the returned message_id.
  3. Receive and verify the signed webhook request.
  4. Check sandbox, sandbox_result, and data.message_id.
  5. Make the webhook handler idempotent before it changes application data.

Use separate batch items when one test run needs more than one result. Do not add scheduled_at unless the schedule itself is part of the test. The deferred result already waits five seconds before it records message.delivered.

Scheduled Sandbox messages

A scheduled message saves its delivery mode and result when Lettermint accepts it. A later project mode change does not change the scheduled message.

The normal schedule limits and lifecycle actions still apply. You can reschedule or cancel a Sandbox message in the same way as a Live message. See Scheduling for the supported time formats and actions.

Limits and safe use

Sandbox mode accepts up to 100 recipients per team per minute. The limit includes Sandbox messages from all projects in the team. Each message can have up to 50 recipients across To, Cc, and Bcc. The maximum message size is 25 MB, including attachments. See Sending limits for the other request limits.

When a team exceeds the Sandbox rate limit, the Sending API returns 429 Too Many Requests with a Retry-After header. A single request or batch with more Sandbox recipients than the per-minute limit can never be accepted, so it returns 422 instead; split it into smaller requests. An SMTP submission over the limit receives a temporary (4xx) failure. Retry after the wait time in Retry-After, or after your SMTP client's normal retry delay.

Use these rules in test systems:

  • Check the project delivery mode before a test run.
  • Do not use sandbox_result as a per-message safety control for a Live project. Lettermint rejects it.
  • Treat webhook calls as real external actions, even when the email delivery is simulated.
  • Use test data when message content or recipient addresses are sensitive. Lettermint stores the submitted content and addresses under the normal retention rules.
  • Verify domains before you switch to Live.
  • Keep Live and Sandbox project tokens in separate environment variables.

For a one-off delivery test in a Live project, you can also use Lettermint test addresses. Use Sandbox mode when you need arbitrary recipients, unverified sender domains, batch cases, per-recipient results in one message, or a complete project boundary for CI.