Skip to content
MailHaap
[01]Getting started

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.

env
export MAILHAAP_API_KEY="mh_test_a1b2c3d4e5f6g7h8i9j0k1"

2. Send

send.sh
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.

202 Accepted
{
  "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.

Simulator addresses
AddressOutcome
[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

A test key accepts identical requests against the same code path. The only difference is that nothing is delivered and nothing is billed. If it works in test mode, the live call is the same call with a different key.