SMTP catcher
What it is
The SMTP catcher is a mail server for your test, QA and staging systems that never delivers anything. Change only the SMTP settings of the app (host, port, user name, password) to ours: every message it sends, to any address, lands in one MailPier environment, where you read it in the web inbox or through the API. Real customers in a copied database can never get a test mail.
- Every recipient is rewritten into the environment you log in to:
jane@contoso.combecomesjane@contoso.com.qa.acme.mailpier.ioand the inbox shows "intended for jane@contoso.com" with a "via SMTP catcher" badge. Addresses already in that environment are kept as they are. - The message itself is stored as your app sent it: the To and Cc headers are not changed. Two headers are added on top:
X-MailPier-Catcher(the SMTP user name) andX-MailPier-Original-Recipients(the addresses your app used). - The plan's limits apply as for any other mail: each recipient counts as a message, and the plan's message size is enforced.
- Part of every plan, Free included: the plan's message, rate and size limits apply. See /pricing.
Set it up
- Open the environment that should catch the mail (for example
qa), tab SMTP. Managers and account admins see it. - Create a password and copy it (it is shown once). An environment can have up to 10 active passwords, for example one per app; revoke one any time.
- Put the settings below into the app's configuration and send a test mail.
Settings
| Host | smtp.mailpier.io |
|---|---|
| Port | 587 with STARTTLS (recommended), 2525 with STARTTLS (when 587 is blocked), or 465 with TLS from the start ("SSL") |
| User name | <environment>.<account>, for example qa.acme; for main just acme |
| Password | a password from the SMTP tab, mps_ followed by 40 letters and digits |
| Authentication | PLAIN or LOGIN. Offered only after TLS; mail without a login is refused (530) |
| Limits | 50 recipients per message; message size as your plan (25 MB at most) |
.NET
appsettings (keep the password in user secrets, a key vault or a pipeline secret, not in the file):
"Smtp": {
"Host": "smtp.mailpier.io",
"Port": 587,
"UserName": "qa.acme",
"Password": "mps_..."
}
MailKit (recommended):
// dotnet add package MailKit
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
var message = new MimeMessage();
message.From.Add(MailboxAddress.Parse("noreply@yourapp.example"));
message.To.Add(MailboxAddress.Parse("jane@contoso.com")); // caught as jane@contoso.com.qa.acme.mailpier.io
message.Subject = "Welcome";
message.Body = new TextPart("plain") { Text = "Hello Jane" };
using var client = new SmtpClient();
await client.ConnectAsync(config["Smtp:Host"], 587, SecureSocketOptions.StartTls); // 465: SecureSocketOptions.SslOnConnect
await client.AuthenticateAsync(config["Smtp:UserName"], config["Smtp:Password"]);
await client.SendAsync(message);
await client.DisconnectAsync(true);
System.Net.Mail.SmtpClient (works on 587/2525 only: it cannot do TLS from the start, so not 465):
using System.Net;
using System.Net.Mail;
using var smtp = new SmtpClient("smtp.mailpier.io", 587)
{
EnableSsl = true, // STARTTLS
Credentials = new NetworkCredential("qa.acme", Environment.GetEnvironmentVariable("MAILPIER_SMTP_PASSWORD")),
};
smtp.Send("noreply@yourapp.example", "jane@contoso.com", "Welcome", "Hello Jane");
Node.js (nodemailer)
// npm install nodemailer
const nodemailer = require("nodemailer");
const transport = nodemailer.createTransport({
host: "smtp.mailpier.io",
port: 587,
secure: false, // STARTTLS on 587/2525; use port 465 with secure: true for TLS from the start
requireTLS: true,
auth: { user: "qa.acme", pass: process.env.MAILPIER_SMTP_PASSWORD },
});
await transport.sendMail({
from: "noreply@yourapp.example",
to: "jane@contoso.com",
subject: "Welcome",
text: "Hello Jane",
});
Python (smtplib)
import os, smtplib, ssl
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "noreply@yourapp.example"
msg["To"] = "jane@contoso.com"
msg["Subject"] = "Welcome"
msg.set_content("Hello Jane")
with smtplib.SMTP("smtp.mailpier.io", 587) as s: # 465: smtplib.SMTP_SSL("smtp.mailpier.io", 465, context=ssl.create_default_context())
s.starttls(context=ssl.create_default_context())
s.login("qa.acme", os.environ["MAILPIER_SMTP_PASSWORD"])
s.send_message(msg)
Java / Spring Boot
application.properties (Spring's JavaMailSender):
spring.mail.host=smtp.mailpier.io
spring.mail.port=587
spring.mail.username=qa.acme
spring.mail.password=${MAILPIER_SMTP_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
PHP / Laravel
.env:
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailpier.io
MAIL_PORT=587
MAIL_USERNAME=qa.acme
MAIL_PASSWORD=mps_...
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@yourapp.example
WordPress (WP Mail SMTP)
- Install and activate the WP Mail SMTP plugin on the test or staging site.
- WP Mail SMTP → Settings → Mailer: Other SMTP.
- SMTP Host
smtp.mailpier.io, Encryption TLS, SMTP Port587, Auto TLS on. - Authentication on, SMTP Username
qa.acme, SMTP Password yourmps_...password (or defineWPMS_SMTP_PASSinwp-config.phpso it is not stored in the database). - Save, then use the plugin's Email Test tab. The test mail appears in the environment's inbox.
Troubleshooting
| Reply | Meaning | What to do |
|---|---|---|
530 5.7.0 | Authentication required | Turn on authentication and TLS in the app (STARTTLS on 587/2525, SSL on 465). We never accept mail without a login. |
535 | Wrong user name or password, or the password was revoked | Check the user name (qa.acme, main: acme) and create a new password on the SMTP tab. A 535 that names your plan means the plan has no SMTP catcher. |
454 4.7.0 | Too many failed logins from your address or for that user name | Wait 10 minutes, then fix the password. The app's retries count as failures too. |
552 5.3.4 | The message is larger than your plan allows (or 25 MB) | Send a smaller message or move to a plan with a larger limit. |
550 5.2.2 | The account's monthly message quota is used up | Wait for next month or change plans (account admins: Billing tab). |
550 5.7.1 | Not allowed by the plan (for example the environment is outside the plan after a downgrade) | Delete environments you no longer need or choose a larger plan. |
553 5.1.3 | A recipient cannot be caught: no domain, an IP address, a quoted or non-ASCII local part, or too long once rewritten | Use plain addresses with a domain name. The other recipients of the message are not affected. |
452 4.5.3 | More than 50 recipients in one message | Split the message. |
451 4.7.1 | Over your plan's messages per second or per day | Your app or library retries later; send more slowly or change plans. |
451 4.3.0 | A temporary problem on our side | Your app or library retries; nothing is lost. |
| certificate error | The app connects to a different host name than the certificate | Use exactly smtp.mailpier.io, not an IP address. |
Questions
Can the catcher send mail to the real recipient by mistake?
No. Every recipient is rewritten into your environment before the message is handed over, and the catcher can only hand mail to the MailPier inbox store. There is no relaying.
Which environment gets the mail?
The one in the user name: qa.acme catches into qa, acme into main.
main sees the mail of all environments anyway.
What happens to a recipient that is already a MailPier address?
An address already in the environment you log in to (for example jane@qa.acme.mailpier.io) is kept as it is.
Any other address, including other MailPier addresses, is rewritten into your environment.
Does each recipient count against the quota?
Yes, as for any mail: a message to three people counts as three messages.