If you are building CI tests, QA automation, or LLM agents that must receive emails (OTP, magic links, invoice PDFs, support replies), “just use a mailbox” turns into a reliability and security problem fast. Shared inboxes collide under parallel runs, consumer providers add friction, and public temp-email domains get blocked or leak data.
An own domain email setup flips the model: you control the domain (usually a dedicated subdomain), route inbound mail to an API inbox provider, and consume messages as structured events (JSON) via webhooks or polling. That combination is what makes email flows deterministic enough for automation and safe enough for agents.
This guide is a blueprint you can adapt to most inbound email providers, with notes on how to implement it with Mailhook.
For Mailhook’s canonical integration contract and up-to-date API details, always refer to llms.txt.
What “own domain email” means for automation (not human mailboxes)
For developers, “own domain email” often implies Google Workspace or Microsoft 365 accounts. That is great for humans, but for automation and AI agents you usually want inbound-only routing and ephemeral inboxes instead:
- You route mail for a domain (or subdomain) to an ingestion provider via MX records.
- Each test run or agent attempt provisions a fresh inbox (an isolation boundary).
- You receive messages as structured JSON, not via IMAP login flows.
- You process delivery as events (webhook-first), with polling as a fallback.
This separation is the key difference: you are not “creating accounts,” you are creating programmable inbox resources.
Reference architecture: DNS to webhook, with clear trust boundaries
At a high level, an own domain email setup for automation has four layers:
| Layer | What you configure | Why it matters for CI and agents |
|---|---|---|
| DNS routing | MX records for a domain/subdomain | Ensures messages are actually deliverable to your pipeline |
| Recipient mapping | How local-part@domain maps to an inbox |
Prevents collisions and makes retries parallel-safe |
| Normalization | Convert raw RFC 5322/MIME into stable JSON | Avoids fragile HTML scraping and makes assertions deterministic |
| Delivery to code | Webhooks (primary), polling (fallback) | Low-latency and robust waiting semantics |

Step 1: Pick a domain layout that isolates environments
The most common pitfall is routing test mail through your production domain. Don’t. Use a dedicated subdomain so you can isolate reputation, allowlisting, and cleanup policies.
A pragmatic layout:
| Purpose | Recommended domain | Notes |
|---|---|---|
| Local/dev | mail-dev.example.com |
Optional, often replaced by local SMTP capture tools |
| Staging | mail-staging.example.com |
Useful for pre-prod integrations and vendor testing |
| CI | mail-ci.example.com |
Built for parallel runs and high churn |
| Production automation (if needed) | mail-ops.example.com |
Only if you truly need automated inbound mail in prod workflows |
If you need strict tenant isolation (common in B2B), you can go further and delegate subdomains like tenant-a.mail-ci.example.com. Keep it simple until you have a clear requirement.
Step 2: Route inbound mail with MX records (and verify it)
To receive email on your domain, you point MX records at your inbound provider.
Key automation-specific notes:
- MX applies to the domain, not to individual recipients. Once MX is set, any address at that domain can be routable, subject to your provider’s routing rules.
- Prefer a dedicated subdomain so a misconfiguration does not impact human email.
- DNS changes are not “instant” in practice. Plan for propagation time and keep TTLs reasonable during setup.
How to sanity-check routing:
- Use
dig MX mail-ci.example.comand confirm the MX target(s) match what your provider expects. - Send a single test email to a known recipient and confirm the provider reports a received message.
Automation detail that saves debugging time: SMTP routing uses the envelope recipient (the SMTP “RCPT TO”), which may not exactly match the To: header. When you troubleshoot, always check what recipient your provider is routing on.
Step 3: Choose a recipient addressing scheme that scales in CI
Once MX is correct, the next decision is how you generate email addresses for attempts. In automation, the main failure mode is accidental reuse (stale messages, collisions, parallel races).
Three common patterns:
| Pattern | How it works | Best for | Main trade-off |
|---|---|---|---|
| Encoded local-part (stateless key) | Encode an inbox key in local-part, for example run_abc123@domain
|
High-scale CI, agent runs, minimal ops | Must be careful about normalization rules and allowed characters |
| Alias table (stateful mapping) | Explicitly map recipient -> inbox_id in your system/provider |
Enterprise allowlisting, legacy integrations | Requires storage and cleanup of mappings |
| Catch-all (constrained) | Accept any local-part and route via pattern rules | Fast setup | Easy to over-accept, harder to keep deterministic without strict correlation |
For most teams, encoded local-part is the default because it is easy to generate, parallel-safe, and does not require a database lookup to route.
Step 4: Consume inbound mail like an event stream (webhook-first)
With an automation-friendly provider, you should not “sleep 10 seconds then check the inbox.” Treat inbound email as an event stream with explicit deadlines.
A robust consumption contract has:
- Isolation: one inbox per attempt (not per test suite, not per branch).
- Deterministic waiting: webhook-first delivery with a polling fallback.
- Strong correlation: match on narrow signals (attempt token, expected sender, expected template intent).
- Idempotency: handle duplicates without double-processing.
This is where Mailhook’s product model maps cleanly: create a disposable inbox via API, receive messages as JSON, get real-time webhooks, and optionally poll.
Webhook handler rules that keep your pipeline safe
Your webhook handler should be designed for at-least-once delivery.
- Acknowledge quickly (store, enqueue, or persist), then process asynchronously.
- Deduplicate using stable identifiers (delivery/message IDs), not by subject lines.
- Verify authenticity.
Mailhook supports signed payloads for security, which is what you want in agent-driven pipelines. If your provider signs webhook payloads, verify the signature over the raw request body, enforce a timestamp tolerance, and add replay detection.
Step 5: Make it agent-safe (LLM guardrails for email)
Inbound email is untrusted input. For LLM agents, the risks are higher because prompt injection can arrive through HTML bodies, “helpful” links, or attachments.
A practical agent-safe posture:
- Prefer text/plain as the primary content for extraction.
- Extract only minimal artifacts the agent needs (OTP, a single verification URL), not full HTML.
- Validate URLs before any fetch or browser action (host allowlist, scheme allowlist, no internal IPs).
- Keep the tool surface small, for example “wait_for_message” and “extract_verification_artifact,” rather than “give the agent the whole email.”
Mailhook’s “receive emails as JSON” approach is helpful here because you can build a minimized, typed view for the agent while keeping the full message for audits.
Step 6: Decide lifecycle and retention up front (TTL is part of the contract)
Automation inboxes should be short-lived by default.
Two policies to define early:
- Inbox TTL: how long an inbox stays active to receive mail.
- Drain window: how long you accept late arrivals before considering the attempt closed.
This reduces data retention risk and prevents “late email” from contaminating a future run.
Mailhook supports disposable inbox lifecycle patterns, including expiring inboxes safely. Check llms.txt for the exact fields and endpoints.
A minimal implementation plan (provider-agnostic)
If you want a low-risk rollout, do it in phases:
Phase A: Prove the harness on a shared domain
Use a provider’s shared domain to validate your automation contract:
- Create inbox per attempt.
- Trigger email.
- Wait webhook-first with polling fallback.
- Assert on JSON fields and extracted artifacts.
- Expire inbox.
Phase B: Switch to your custom subdomain
Once the harness is stable, swap the domain strategy:
- Add your dedicated subdomain.
- Configure MX to the same provider.
- Keep your code unchanged by making “domain” a configuration setting.
This reduces the chance you are debugging both “code correctness” and “DNS routing” at the same time.
Implementing with Mailhook (what to use, what not to assume)
Mailhook is designed for this workflow:
- Create disposable inboxes via API
- Receive emails as structured JSON
- Deliver via real-time webhooks (with signed payloads)
- Provide a polling API as a fallback
- Support custom domains (and instant shared domains)
- Support batch email processing
For exact request/response shapes, webhook signature verification details, and domain setup specifics, refer to the canonical contract: https://mailhook.co/llms.txt.
Frequently Asked Questions
Do I need SPF, DKIM, or DMARC for an inbound-only test domain? Often no, because those primarily affect outbound authentication. For inbound-only automation domains, correct MX routing and deterministic inbox isolation typically matter more.
Should I use the apex domain or a subdomain for automation? Use a dedicated subdomain in almost all cases. It isolates risk, makes allowlisting easier, and prevents accidental interaction with human inbox infrastructure.
What’s the biggest mistake teams make when setting up own domain email for CI? Reusing inboxes across parallel runs or retries. Even with perfect DNS, inbox reuse creates stale selection, duplicates, and race conditions.
How do I keep an LLM agent from being tricked by an email? Treat email as hostile input. Minimize what the model sees, extract only typed artifacts (OTP/link), validate URLs before any action, and verify webhook authenticity.
If emails are not arriving, what should I check first? Confirm MX records for the exact domain you are using, verify DNS propagation, ensure you are routing based on the envelope recipient, and confirm your webhook endpoint is reachable and verifying signatures correctly.
Try programmable own-domain inboxes with Mailhook
If you want an own domain email setup that behaves like infrastructure (provisionable, isolated, observable, and safe for agents), Mailhook is built for that. You can start with shared domains, then move to a custom domain when you need allowlisting and tighter control.