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
| Area | Sandbox behavior |
|---|---|
| Outbound delivery | Lettermint records a simulated result and does not connect to a recipient mail server. |
| Sender domains | You 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 processing | Authentication, address, content, attachment, size, recipient, schedule, and idempotency checks still apply. |
| Routes | Transactional and Broadcast routes can send Sandbox messages. Inbound routes are not affected. |
| Events and webhooks | Lettermint creates message history and sends signed webhook requests with the normal event names. |
| Billing and statistics | Sandbox messages do not use your email quota and do not affect billable usage, delivery statistics, reputation, warmup, or Live sending limits. |
| Suppressions | Sandbox messages ignore Live suppression lists and do not add, change, or remove suppression records. |
| Project limits | A Sandbox project counts toward your project limit in the same way as a Live project. |
| Retention | Lettermint 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:
- Open Projects and select Create project.
- Select Sandbox as the delivery mode.
- Select Transactional, Broadcast, or Both as the email type.
- Enter the project name, select the SMTP setting, and create the project.
The Team API also accepts delivery_mode when you create a project:
Code
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 state | Result after a mode change |
|---|---|
| Accepted in Live mode | The message stays Live and can still be delivered. |
| Accepted in Sandbox mode | The message stays Sandbox and remains simulated. |
| Scheduled in Live mode | The message uses Live delivery when its schedule releases it. |
| Scheduled in Sandbox mode | The 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:
Code
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.
Code
The accepted response of a Sandbox send includes sandbox: true and the message-level sandbox_result:
Code
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.
Code
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
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 part | Result |
|---|---|
ok | delivered |
softbounce | soft_bounced |
hardbounce | hard_bounced |
spamcomplaint | spam_complaint |
dsn | hard_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:
| Precedence | Source |
|---|---|
| 1 | The recipient's test address |
| 2 | The message-level sandbox_result |
| 3 | delivered (default) |
Example: API
Code
hardbounce+signup@lettermint.dev simulates hard_bounced. recipient@example.com simulates delivered, the message-level result.
Example: SMTP
Code
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_result | Event sequence |
|---|---|
delivered | message.sent, then message.delivered |
hard_bounced | message.sent, then message.hard_bounced |
soft_bounced | message.sent, then message.soft_bounced |
deferred | message.sent, message.soft_bounced, then message.delivered after five seconds |
failed | message.sent, message.soft_bounced, then message.failed |
suppressed | message.suppressed |
spam_complaint | message.sent, message.delivered, then message.spam_complaint |
auto_replied | message.sent, message.delivered, then message.auto_replied |
opened | message.sent, message.delivered, then message.opened |
clicked | message.sent, message.delivered, message.opened, then message.clicked |
unsubscribed | message.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:
Code
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:
| Filter | Events sent to the webhook |
|---|---|
| Live | Live events only |
| Sandbox | Sandbox events only |
| Both | Live 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:
- Send a message with the result that the test needs.
- Store the returned
message_id. - Receive and verify the signed webhook request.
- Check
sandbox,sandbox_result, anddata.message_id. - 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_resultas 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.