Learn how to use EmailSpect's SMTP behavior simulation and webhook features
EmailSpect uses plus addressing to simulate different SMTP behaviors.
Send an email to any address with a +command suffix to trigger the behavior.
Example:
test+550@yourdomain.com
This will simulate a "550 Mailbox unavailable" bounce response.
To send emails directly to EmailSpect from your local application, configure your app to use our SMTP server.
Laravel .env configuration:
MAIL_MAILER=smtp MAIL_HOST=retty.email MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null
Why port 2525 instead of 25?
Most ISPs and cloud providers block outbound connections to port 25 to prevent spam. Port 2525 is a common alternative that bypasses these restrictions. Both ports are supported by EmailSpect.
EmailSpect acts as the receiving mail server for your domain. When you send an email through your provider, here's what happens:
Your Application Sends Email
Your app (Laravel, Node.js, etc.) sends an email through your SMTP provider (Postmark, SendGrid, Mailgun, etc.) to an address like test+550@retty.email
Provider Delivers to EmailSpect
Your email provider looks up the MX record for the domain and connects to EmailSpect's SMTP server to deliver the email.
EmailSpect Receives & Logs
EmailSpect receives the email, parses the +550 behavior command, and logs the email to the inbox for your review.
EmailSpect Simulates Response
EmailSpect returns the simulated SMTP response (e.g., 550 5.1.1 Mailbox unavailable) to your email provider during the SMTP transaction.
Provider Reports the Bounce
Your email provider (Postmark, etc.) sees the 550 response and reports it as a bounce through their dashboard and webhooks, just like a real bounce would occur.
Key Insight: This lets you test how your application handles bounce notifications from your email provider without affecting real users. The entire flow is identical to a real-world bounce scenario.
Control how EmailSpect responds to incoming emails using plus addressing.
By default, all emails are accepted normally with a 250 OK response.
Add a +command to the email address to simulate different SMTP responses.
Unrecognized suffixes are ignored. Sending to test+anything@domain.com
will be accepted normally if anything isn't a known command.
This lets you use plus addressing for organization (like Gmail) while reserving specific commands for behavior simulation.
Normal delivery - email is accepted and stored.
| Address | SMTP Code | Description |
|---|---|---|
| test@domain.com | 250 2.0.0 | Email accepted and delivered (default behavior) |
| test+spam@domain.com | 250 2.0.0 | Email accepted but marked as spam |
| test+blackhole@domain.com | 250 2.0.0 | Email accepted but silently discarded (not stored) |
These errors indicate the delivery permanently failed and should not be retried.
| Command | Aliases | SMTP Code | Description |
|---|---|---|---|
| +550 | +bounce, +reject, +nouser | 550 5.1.1 | Mailbox unavailable - user does not exist |
| +551 | +moved | 551 5.1.6 | User not local / mailbox has moved |
| +552 | +full, +quota | 552 5.2.2 | Mailbox full - exceeded storage allocation |
| +553 | +notallowed | 553 5.1.3 | Mailbox name not allowed |
| +554 | +failed | 554 5.7.1 | Transaction failed - message rejected |
These errors indicate a temporary issue - the sender should retry later.
| Command | Aliases | SMTP Code | Description |
|---|---|---|---|
| +421 | +unavailable, +down | 421 4.3.2 | Service temporarily unavailable |
| +450 | +busy | 450 4.2.1 | Mailbox temporarily unavailable |
| +451 | +tempfail, +retry | 451 4.3.0 | Local error in processing |
| +452 | +nostorage | 452 4.3.1 | Insufficient system storage |
Advanced testing scenarios for edge cases and specific behaviors.
| Command | Aliases | Description |
|---|---|---|
| +timeout | +hang, +freeze | Server never responds - connection hangs until client timeout |
| +greylist | +graylist | First attempt returns 450, retry within 5 minutes succeeds |
| +blackhole | +drop, +null, +devnull | Accepts email but does not store it (returns 250 OK) |
| +spam | +junk | Accepts and stores email, marks as spam |
| +delay{N} | +slow{N} | Delays SMTP response by N seconds (max 120) |
Delay Example:
test+delay30@yourdomain.com
This will delay the SMTP response by 30 seconds, useful for testing timeout handling.
Receive real-time notifications when emails arrive.
Configure webhooks per domain in Domain Settings. When an email is received, we'll send a POST request to your configured URL.
{
"event": "email.received",
"timestamp": "2026-02-06T22:10:00+00:00",
"email": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"message_id": "<abc123@example.com>",
"from_address": "sender@example.com",
"from_name": "John Doe",
"to_address": "test@yourdomain.com",
"subject": "Hello World",
"text_body": "Email content...",
"html_body": "<p>Email content...</p>",
"has_attachments": false,
"received_at": "2026-02-06T22:10:00+00:00"
},
"smtp": {
"behavior": "550",
"behavior_label": "Bounced (550)",
"response_code": 550,
"response_message": "550 5.1.1 Mailbox unavailable",
"is_delivered": false
},
"inbox": {
"uuid": "...",
"address": "test@yourdomain.com"
},
"domain": {
"name": "yourdomain.com"
}
}
If you've configured a webhook secret, verify the signature to ensure the request is authentic:
// PHP Example $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_EMAILSPECT_SIGNATURE']; $expected = hash_hmac('sha256', $payload, $secret); if (hash_equals($expected, $signature)) { // Signature valid }
Ideas for testing your email functionality.
Send to user+550@domain to verify your app correctly handles permanent bounces and updates user records.
Send to user+451@domain to test that your app properly retries on temporary failures.
Send to user+timeout@domain to verify your app handles SMTP timeouts gracefully.
Send to user+delay10@domain to test how your app handles slow SMTP responses.