The email-testing home: configures and runs Mailpit - modern dev-mailbox server for SMTP testing with built-in REST API for assertions; default SMTP `1025` + Web UI `8025`; Chaos mode (configurable SMTP errors for resilience testing), message tagging, search filters. Carries the end-to-end email-flow workflow (multipart body, link-rewrite resolution, unsubscribe per RFC 8058, bounce + complaint webhooks) in references/email-flows.md and the legacy MailHog capture patterns + migration path in references/mailhog-legacy.md. Use when developing or testing email-sending code locally / in CI - SMTP capture, full-flow assertions, or migrating an existing MailHog deployment.
72
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Per github.com/mailhog/MailHog:
"MailHog is an email testing tool for developers" that allows you to "Configure your application to use MailHog for SMTP delivery" and "View messages in the web UI, or retrieve them with the JSON API."
MailHog is legacy as of the mid-2020s - Mailpit (the host SKILL.md) succeeded it with richer API + active maintenance. This reference covers MailHog for existing deployments + provides migration guidance to Mailpit.
For new projects, use the host SKILL.md (Mailpit).
1025, web UI + JSON API on 8025.localhost:1025 with SMTP auth disabled.DELETE /api/v1/messages), trigger the action that sends mail, then poll APIv2 (/api/v2/messages or /api/v2/search) until the message lands.Content.Headers.Subject array - see the Worked example.mailhog -invite-jim); when leaving MailHog, follow Migrating to Mailpit.Per mh-gh:
# Go install
go install github.com/mailhog/MailHog@latestDocker:
docker run -d \
--name mailhog \
-p 1025:1025 \
-p 8025:8025 \
mailhog/mailhogPer mh-gh:
| Port | Service |
|---|---|
| 1025 | SMTP server |
| 8025 | HTTP server (UI + APIv1 + APIv2) |
Same pattern as Mailpit (since both expose unauthenticated SMTP on 1025 by default):
smtp:
host: localhost
port: 1025
auth: nonePer mh-gh MailHog has both APIv1 + APIv2; APIv2 is the modern one. Endpoints:
| Endpoint | Use |
|---|---|
GET /api/v2/messages | List captured messages (paginated) |
GET /api/v2/messages?limit=N&start=M | Pagination |
GET /api/v2/search?kind=to&query=alice@x.com | Search by recipient / subject / containing |
DELETE /api/v1/messages | Clear all messages (uses APIv1; APIv2 has no delete) |
The MailHog message structure is more nested than Mailpit's:
Content.Headers.Subject is an array, not the flat Subject
field Mailpit exposes. Walk the nested path or the assertion
silently fails.
Capture and assert a single password-reset email end to end. Clear the mailbox first so a stale message can't satisfy the assertion, trigger the app action, poll APIv2 until the message lands, then assert on its subject and body:
import requests
BASE = "http://localhost:8025"
def test_password_reset_via_mailhog():
requests.delete(f"{BASE}/api/v1/messages") # clear (APIv1 - no APIv2 delete)
trigger_password_reset("alice@example.com") # app under test sends the email
msg = poll_for_message(BASE, to="alice@example.com") # GET /api/v2/search?kind=to&query=...
assert msg["Content"]["Headers"]["Subject"][0] == "Reset your password"
assert "/reset?token=" in msg["Content"]["Body"]poll_for_message retries GET /api/v2/search?kind=to&query=alice@example.com
until a message appears, then returns it. Sub-second SMTP delivery
isn't guaranteed, so never assert immediately after triggering.
Per mh-gh: "Chaos Monkey for failure testing" via the Jim component. Jim is configurable via CLI flags or environment:
mailhog -invite-jim # enables the chaos monkeyOnce Jim is invited, MailHog injects failures (random connection
drops, slow responses) per the configured probabilities.
Configuration flags are documented in mailhog -h; the README
references "Introduction to Jim" for details.
For new test work needing chaos, prefer Mailpit's Chaos mode (host SKILL.md Step 5) - it has richer per-recipient configuration.
services:
mailhog:
image: mailhog/mailhog
ports: [1025:1025, 8025:8025]
steps:
- run: pytest tests/integration/email/ -vFor new projects, and for teams ready to leave MailHog, migrate to Mailpit (the host SKILL.md) - the actively maintained successor with a flatter API and richer chaos configuration. The feature-mapping table, the schema-rewrite notes, and the step-by-step cutover live in migrating-to-mailpit.md.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Start new project on MailHog | Legacy; loses richer Mailpit features | Use Mailpit (Migrating to Mailpit + cross-skill) |
| Use APIv1 for new test code | Deprecated by APIv2 (within MailHog) | APIv2 endpoints (Assert via APIv2) |
| Assume MailHog flat schema | MailHog's Content.Headers.Subject is an array | Walk the nested structure (Worked example) |
| Skip per-test message clear | Same problem as Mailpit Step 4; stale messages | DELETE /api/v1/messages in setup |
| Skip Jim coverage | Same as Mailpit Step 5; misses resilience | Enable Jim or migrate to Mailpit Chaos |