Quickstart
Add a domain, publish the DNS records, create an API key, and POST to /v1/emails. On a fresh account this takes under five minutes, and you can do the whole thing in test mode first without a verified domain.
1. Create a key
Keys are scoped, can be pinned to a single domain and an IP allowlist, and are shown exactly once. Start with a test key — test sends need no verified domain, consume no quota, and produce real webhook events on a realistic delay curve.
export MAILHAAP_API_KEY="mh_test_a1b2c3d4e5f6g7h8i9j0k1"2. Send
curl -X POST https://api.mailhaap.com/v1/emails \
-H "Authorization: Bearer $MAILHAAP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-1" \
-d '{
"from": "You <[email protected]>",
"to": ["[email protected]"],
"subject": "It works",
"html": "<p>Hello from MailHaap</p>"
}'You get a 202 with a message id. The send itself happens in a worker — the API never blocks on the provider.
{
"id": "msg_01J8XQ7M2K9PQRSTUV",
"status": "queued",
"created_at": "2026-08-21T16:04:11Z"
}3. Watch what happened
Every message carries a trace id that is queryable in the dashboard, so “why did this email not arrive” is answerable from the message id alone. The simulator addresses below produce each outcome deliberately, so you can prove your bounce and complaint handling works before a real bounce proves it does not.
| Address | Outcome |
|---|---|
| [email protected] | Delivered after ~2 s |
| [email protected] | Hard bounce, SMTP 550 |
| [email protected] | Soft bounce, then delivery on retry |
| [email protected] | Delivered, then a complaint after ~10 s |
| [email protected] | Rejected at accept with recipient_suppressed |
| [email protected] | Delivered after ~90 s |
| [email protected] | Delivered, then open and click events |
Test mode is not a sandbox with different behaviour