Concepts
Understanding MXHook's core concepts helps you configure it effectively.
Domains
A domain represents an email domain that MXHook is authorized to receive mail for. When an email arrives, MXHook checks the recipient's domain against registered domains and rejects mail for unknown domains.
yourdomain.com ✓ registered → accepted
unknown.com ✗ not registered → rejected at SMTP levelDomains are managed via the REST API or the mxhook domain CLI command.
Routes
A route connects a recipient pattern to a webhook URL. When an email matches a route's pattern, MXHook delivers the parsed payload to that route's webhook endpoint.
Each route has:
- Domain — which domain this route applies to
- Recipient pattern — who the email is addressed to (supports exact, wildcard, and plus-addressing)
- Webhook URL — where to deliver the parsed email
- Webhook secret (optional) — used to sign payloads with HMAC-SHA256
Matching Patterns
| Pattern | Matches | Example |
|---|---|---|
user@domain.com | Exact address only | user@domain.com |
*@domain.com | Any recipient on the domain | alice@domain.com, bob@domain.com |
user@domain.com | Plus-addressing variants | user+tag@domain.com → user@domain.com |
Routes are evaluated in priority order. The first matching route wins.
Message Lifecycle
Every email that enters MXHook moves through a series of states:
accepted → queued → parsed → routed → delivered
↓
failed → retried → delivered
↓
dlq| State | Description |
|---|---|
accepted | SMTP server received the message |
queued | Message placed in the processing queue |
parsed | Email parsed into structured data |
routed | Matched to a route with a webhook destination |
delivered | Webhook POST succeeded (2xx response) |
failed | Webhook POST failed, will retry |
retried | Retrying delivery with exponential backoff |
dlq | All retry attempts exhausted, moved to dead letter queue |
Queue
MXHook uses a queue to decouple SMTP ingestion from webhook delivery. This ensures the SMTP server responds quickly and messages are processed reliably.
Two queue backends are supported:
- PostgreSQL (default) — uses
SELECT FOR UPDATE SKIP LOCKEDfor at-most-once delivery - Cloudflare Queues — for serverless/edge deployments
Webhook Delivery
When a message is routed, MXHook:
- Serializes the parsed email as JSON
- Signs the payload with HMAC-SHA256 using the route's secret (if configured)
- POSTs to the webhook URL with the signature in the
X-MXHook-Signatureheader - Retries with exponential backoff on failure (default: 3 attempts)
- Moves to the dead letter queue if all retries are exhausted
Dead Letter Queue
The DLQ captures messages that failed all delivery attempts. You can:
- Inspect failed messages to understand why delivery failed
- Replay messages to re-attempt delivery
- Remove entries once they're handled
See the DLQ guide for details.