I used mail.tm for about a year for disposable inbox needs in my test pipelines. Clean REST API, decent uptime, simple enough to integrate. I did not think too hard about switching.
Then I started hitting its limits in quick succession: no OTP extraction (write your own regex), no WebSocket delivery (poll and wait), no CLI (write your own wrapper), no official SDK (use a random community package that was last updated 18 months ago).
Every project that grew beyond basic message fetching required me to build and maintain tooling that should have been provided by the service. Eventually I switched to FreeCustom.Email and those problems went away. This post explains exactly what changed.
The Gap That Kept Growing
The longer I used mail.tm, the more I noticed what was missing — not bugs, but capabilities that simply did not exist:
| What I wanted | mail.tm | FreeCustom.Email |
|---|---|---|
otp extract command |
✗ write your own regex | ✓ built in |
| Real-time WebSocket delivery | ✗ polling only | ✓ < 200ms |
| Official CLI | ✗ | ✓ fce — open source, MIT
|
| Official TypeScript SDK | ✗ | ✓ |
| Official Python SDK | ✗ | ✓ |
| CI/CD first-class support | ✗ manual | ✓ native |
| AI agent integration | ✗ | ✓ |
| n8n integration | ✗ | ✓ |
mail.tm is not broken — it is just a REST API that stopped evolving. In 2026, if you are doing anything beyond basic email retrieval, you will spend more time building workarounds than writing your actual application.
The CLI Is the Biggest Difference
FreeCustom.Email ships fce — the only official CLI for any disposable email service in the world. That is not marketing — I checked. No other provider has one.
Install it:
# macOS / Linux
curl -fsSL freecustom.email/install.sh | sh
# Homebrew
brew tap DishIs/homebrew-tap && brew install fce
# Windows (Scoop)
scoop bucket add fce https://github.com/DishIs/scoop-bucket && scoop install fce
# Windows (Chocolatey)
choco install fce
# npm (all platforms)
npm install -g fcemail@latest
# Go (all platforms)
go install github.com/DishIs/fce-cli@latest
Source and all releases: github.com/DishIs/fce-cli/releases
Login once — it opens your browser, authenticates, and saves your key to the OS keychain automatically:
fce login
# No manual token copying. Key auto-updates with your plan.
Then:
fce dev
· Temporary inbox: dev-fy8x@ditcloud.info
✓ Watching for emails...
· Waiting for emails… (press Ctrl+C to stop)
────────────────────────────────────────────────────
ID JpW3DImT3
FROM noreply@myapp.com
SUBJ Your verification code
TIME 20:19:54
────────────────────────────────────────────────────
Inbox created. WebSocket open. Emails appearing in real time. One command.
With mail.tm, the equivalent required: create an account, get a JWT, store it, poll GET /messages, parse the email body, extract the OTP with regex. That is a script, not a command.
OTP Extraction: mail.tm vs fce
This was my biggest pain point with mail.tm.
With mail.tm, you fetch the full message body and parse it yourself:
// What I was doing with mail.tm
const messages = await fetch('https://api.mail.tm/messages', {
headers: { Authorization: `Bearer ${jwt}` }
}).then(r => r.json());
const body = messages['hydra:member'][0]?.text ?? '';
// Six patterns because different services format OTPs differently
const otp = body.match(/\b(\d{6})\b/)
?? body.match(/code[:\s]+(\d{6})/i)
?? body.match(/OTP[:\s]+(\d{4,8})/i)
?? body.match(/verification[:\s]+(\d{4,8})/i)
// ... and it still breaks when templates change
?.[1];
With FreeCustom.Email:
fce otp dev-fy8x@ditcloud.info
────────────────────────────────────────────────
OTP
────────────────────────────────────────────────
OTP · 212342
From · noreply@myapp.com
Subj · Your verification code
Time · 20:19:54
Or in code:
const otp = await fce.otp.waitFor(inbox, { timeout: 30_000 });
// → "212342"
The OTP extraction engine handles numeric codes, alphanumeric tokens, magic links, HTML emails, and multi-part MIME — without regex that breaks when a template changes.
Migration from mail.tm
If you are already using mail.tm in a project, migration is maybe an hour of work.
Step 1: Get an FCE account and key
curl -fsSL freecustom.email/install.sh | sh
fce login
# Key saved automatically to keychain
# For CI: export FCE_API_KEY=... (get from dashboard)
Step 2: Replace account creation with inbox registration
Before:
// mail.tm — must create an account with a password, then get a JWT
const { id: accountId } = await fetch('https://api.mail.tm/accounts', {
method: 'POST',
body: JSON.stringify({ address: inbox, password: 'SomePass123' }),
}).then(r => r.json());
const { token: jwt } = await fetch('https://api.mail.tm/token', {
method: 'POST',
body: JSON.stringify({ address: inbox, password: 'SomePass123' }),
}).then(r => r.json());
After:
// FCE — one call, no password, no JWT management
const fce = new FreecustomEmailClient({ apiKey: process.env.FCE_API_KEY });
await fce.inboxes.register(inbox);
Step 3: Replace polling + regex with OTP extraction
Before:
// Poll and regex
let otp = null;
for (let i = 0; i < 30 && !otp; i++) {
await sleep(2000);
const { 'hydra:member': messages } = await fetch('https://api.mail.tm/messages', {
headers: { Authorization: `Bearer ${jwt}` },
}).then(r => r.json());
if (messages.length > 0) {
const match = messages[0].text.match(/\b(\d{6})\b/);
otp = match?.[1];
}
}
After:
const otp = await fce.otp.waitFor(inbox, { timeout: 30_000 });
Step 4: Replace account deletion with inbox removal
Before:
await fetch(`https://api.mail.tm/accounts/${accountId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${jwt}` },
});
After:
await fce.inboxes.unregister(inbox);
// Or: fce inbox remove inbox@ditmail.info
Real-Time vs Polling
mail.tm has no WebSocket option. You poll GET /messages on a loop, which means:
- Minimum 2–5 second delay per check
- Wasted API quota on empty responses
- CI pipelines that feel slow for no reason
FreeCustom.Email delivers emails via WebSocket on Startup plan+. They appear in your terminal (or SDK callback) in under 200ms:
fce watch test@ditmail.info
In CI tests, this is the difference between a 30-second polling loop and near-instant OTP receipt.
Authentication: JWT Hell vs Bearer Token
This is a minor thing but I noticed it every time. mail.tm requires you to POST credentials to get a JWT, then use that JWT for all subsequent requests. JWTs expire. You have to handle refresh. You are managing a password for a test inbox.
FreeCustom.Email uses a simple Bearer token (fce_your_key). Get it from the dashboard, set it once as FCE_API_KEY, done. Never expires unless you rotate it. No password to manage.
The Automation Ecosystem That mail.tm Has None Of
Beyond the core API, FreeCustom.Email is building something mail.tm has not touched: an automation ecosystem.
Today:
- fce CLI — the only disposable email CLI
- Official JS/TS and Python SDKs
- n8n visual workflow integration
- OpenClaw AI agent support
Coming Q2 2026:
- Native Make (Integromat) modules
- Native Zapier triggers and actions
Full details: freecustom.email/api/automation
Pricing
| Plan | Price | WebSocket | OTP extraction |
|---|---|---|---|
| Free | $0 | ✗ | ✗ |
| Developer | $7/mo | ✗ | ✗ |
| Startup | $19/mo | ✓ | ✗ |
| Growth | $49/mo | ✓ | ✓ |
| Enterprise | $149/mo | ✓ | ✓ |
Full details: freecustom.email/api/pricing
Quick Decision Guide
| Use case | Best option |
|---|---|
| Local dev: watch for emails | fce dev |
| Shell script / CI |
fce inbox add random + fce otp
|
| Playwright / Vitest |
freecustom-email npm SDK |
| pytest / Selenium |
freecustom-email pip SDK |
| AI agent automation | OpenClaw + fce login
|
| Visual no-code workflow | n8n (now) / Make or Zapier (Q2 2026) |
Links
- 🖥️ CLI docs: freecustom.email/api/cli
- 📦 GitHub: github.com/DishIs/fce-cli
- 🚀 Releases: github.com/DishIs/fce-cli/releases
- 🤖 Automation: freecustom.email/api/automation
- 📖 API docs: freecustom.email/api/docs
- 💬 Discord: discord.com/invite/Ztp7kT2QBz
If you are currently using mail.tm, Mailinator, or Guerrilla Mail in your projects, I am curious what is holding you back from switching to something with better tooling. Is it the migration effort, pricing, or something the other services do that FCE does not?
Top comments (0)