Email delivery
Send an email, then read what happened to it:
hail email send --to ada@example.com --subject "Hi" --body "Hello" --recipient-consent
hail email events <email-id>Same send over HTTP (OpenAPI: POST /v1/emails):
curl -X POST "$HAIL_API_URL/v1/emails" \
-H "Authorization: Bearer $HAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": ["ada@example.com"], "subject": "Hi", "body_text": "Hello", "recipient_consent": true}'What Hail does when you send
These apply to mail you send with POST /v1/emails or through an agent. Forwarded mail and Hail's own notices follow other rules (architecture).
- Your domain signs the mail. A custom domain gets DKIM and a MAIL FROM on
send.<domain>, so SPF and DKIM align with yourFrom.hail email domain dns-check <id>checks DKIM, MAIL FROM and DMARC, and gives a DMARC record to publish if you have none. - One-click unsubscribe. An email with exactly one recipient carries
List-UnsubscribeandList-Unsubscribe-Post(RFC 8058). The link works for one year. A click on it, or the Unsubscribe button in Gmail or Yahoo, adds the address to your suppression list. An email with two or more recipients carries no unsubscribe header. - Hard bounces and spam complaints stop future sends. The addresses named in the bounce or complaint are added to your suppression list. A soft bounce (mailbox full, server busy) is recorded as an event only.
- Send limits. Each organization has an hourly and a daily send cap. Over the cap,
POST /v1/emailsreturns403.
The suppression list belongs to your organization. Another organization on Hail can still send to that address.
Blocked sends
A send to a suppressed address returns 403 with the reason:
{ "detail": "recipient 'bob@example.com' is suppressed (hard_bounce)" }Reasons: recipient_unsubscribed, hard_bounce, spam_complaint. One suppressed recipient in to, cc or bcc blocks the whole send.
List your suppressions, and remove an address:
hail email suppressions list
hail email suppressions delete bob@example.comEvents and stats
Each email has a timeline of events: sent, delivered, delivery_delayed, bounced, complained, rejected, opened, clicked. Open and click tracking needs body_html. Get events pushed to you with a webhook (email.delivered, email.bounced, email.complained, ...).
Totals and rates for a time range:
hail email stats
curl "$HAIL_API_URL/v1/emails/stats?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z" \
-H "Authorization: Bearer $HAIL_API_KEY"Code: email_delivery_events.py (bounces, complaints), unsubscribe.py, compliance_gate.py (suppression and limits).
Webhooks
When a subscribed event occurs — inbound mail or SMS, a delivery report, a call outcome — Hail POSTs a signed JSON event to your URL. Verify the X-Hail-Signature header against the once-shown secret. Return any 2xx, and you are done. Hail retries a non-2xx response (or a timeout) on a fixed ladder.
react-email
Hail doesn't render React or store templates — you render a react-email component to a string and send that string. The body goes out exactly as given: a full <!DOCTYPE html>…</html> document passes through unchanged.