Custom domains make temporary inboxes feel less like a testing hack and more like infrastructure. They let you use addresses under a domain you control, separate environments cleanly, support allowlisting, and keep agent or QA traffic away from human mailboxes.
But the setup is not just “add an MX record.” For automation, a custom domain has to route messages into short-lived inboxes, expose those messages as structured data, and give your test runner or LLM agent a safe way to wait for the exact email it needs.
This guide covers what to set up before you run custom domains for temp inboxes in CI, signup verification, agent tooling, or client operations.
The target architecture
A reliable custom-domain temp inbox flow has four moving parts: DNS, inbox provisioning, message delivery, and lifecycle control.
The basic flow looks like this:
- Your code creates a disposable inbox via API.
- The API returns an email address on your custom domain plus an inbox identifier.
- The system under test sends a verification email, OTP, magic link, or operational message to that address.
- Your automation receives the email as structured JSON through a webhook or polling API, extracts the minimal artifact, then expires the inbox.

The key design principle is simple: treat the inbox as the resource, not just the email string. An address like [email protected] is useful only if it maps to a known inbox_id, lifecycle policy, and delivery contract.
1. Choose a domain layout that limits blast radius
For most teams, the safest choice is a dedicated subdomain, not the root domain and not the same domain used for employee email.
If your company uses example.com for production users and staff, route temp inboxes through something like qa-mail.example.com, agent-mail.example.com, or staging-inbox.example.com. This keeps inbound automation traffic separate from human mail, reduces accidental routing changes, and makes DNS ownership clearer.
| Layout | Best for | Watch out for |
|---|---|---|
| Shared provider domain | Fast prototypes, local tests, early CI | May be blocked by some third-party services or harder to allowlist |
| Dedicated custom subdomain | CI, QA, LLM agents, enterprise allowlisting | Requires DNS setup and ownership discipline |
| Root domain | Rarely recommended for temp inboxes | Higher risk of disrupting human or production email |
| Environment subdomains |
qa, staging, agent, tenant-specific workflows |
More DNS records and governance to maintain |
A good default is one subdomain per environment class. For example, use one for CI and one for agent-driven workflows if those systems have different risk profiles, retention rules, or allowlisting needs.
2. Configure inbound DNS deliberately
Email delivery depends on DNS. For inbound mail, the most important record is MX, which tells sending mail servers where to deliver messages for a domain. The SMTP specification describes how MTAs use MX records when routing mail, and you can read the underlying standard in RFC 5321.
When using a temp inbox provider, you should use the MX values supplied by that provider. Do not guess hostnames or priorities. The exact target depends on the service and domain verification flow.
For a custom-domain temp inbox setup, prepare these DNS items:
- A dedicated subdomain for automation traffic.
- Provider-supplied MX records for that subdomain.
- A low DNS TTL during rollout, if your DNS provider allows it.
- A DNS owner or team responsible for future changes.
- A rollback plan that restores the previous MX records if delivery fails.
For inbound-only test domains, SPF, DKIM, and DMARC usually do not determine whether you can receive mail. They matter when the domain also sends email, or when you are testing sender authentication. If your temp inbox subdomain is only receiving verification messages, focus first on MX routing, recipient mapping, and message consumption.
After publishing DNS, verify it from more than one network or resolver. A basic check looks like this:
dig MX qa-mail.example.com
You are looking for the provider-supplied MX targets to appear consistently. DNS propagation can take time, especially if the previous TTL was high.
3. Define the recipient namespace before creating inboxes
Custom domains give you control over the domain part of the address. You still need a strategy for the local part, the text before @.
The local part is where many automation systems create collisions. If two tests reuse [email protected], the second test may read the first test’s email. If a retry reuses the same address, it may process a stale magic link. If an LLM agent can invent recipient names freely, it can create hard-to-debug routing behavior.
Decide how recipient addresses are generated before you start sending production-like traffic.
| Recipient pattern | How it works | Good fit |
|---|---|---|
| Encoded local-part | Put a short run, attempt, or inbox key in the address | Stateless routing and high-volume CI |
| Alias table | Store recipient-to-inbox mappings in a database or provider | Human-readable aliases and compatibility cases |
| Constrained catch-all | Accept many local parts, then match with strict rules | Migration, debugging, and controlled test environments |
For deterministic automation, the best default is still one temporary inbox per attempt. The local part can include a short identifier for debugging, but the source of truth should be the inbox descriptor returned by the API.
A practical address might look like this:
[email protected]
That address should map to a specific inbox, a specific test attempt, and a specific lifecycle. Your code should not search across every message on the domain. It should read from the inbox it created.
4. Store an inbox descriptor, not just an address
The most common mistake in automated email flows is storing only the email address. For temp inboxes, store a descriptor that your test runner, agent, or backend worker can use later.
A useful descriptor includes:
-
email, the full custom-domain address. -
inbox_id, the provider or application identifier for the inbox. -
domain, the custom domain or subdomain used. -
run_idorattempt_id, so logs can be correlated. -
created_atandexpires_at, for lifecycle decisions. -
webhook_delivery_idor message IDs when emails arrive. - A state such as
active,draining, orclosed.
In provider-agnostic pseudocode, this looks like:
const inbox = await emailProvider.createInbox({
domain: "qa-mail.example.com",
purpose: "signup_verification",
metadata: {
run_id: process.env.CI_RUN_ID,
attempt_id: attemptId
}
})
await app.signup({ email: inbox.email })
const message = await waitForVerificationEmail({
inbox_id: inbox.inbox_id,
deadline_ms: 60_000
})
Mailhook is built around this programmable model: you can create disposable inboxes via API, receive messages as structured JSON, and use custom domains when your workflow needs domain control. For exact integration details, use the Mailhook llms.txt integration reference, which is the best machine-readable starting point for agents and implementation tools.
5. Set up webhook-first delivery with polling fallback
For temp inboxes, delivery should be event-driven when possible. A real-time webhook can notify your system as soon as a message arrives, which reduces fixed sleeps and makes parallel CI less flaky.
Webhook handlers should be intentionally boring. Verify the payload, record the delivery, enqueue processing, and return quickly. Do not run the whole verification flow inside the HTTP handler if it can time out or retry unpredictably.
A production-ready webhook receiver should handle these concerns:
- Signature verification over the raw request body.
- Replay protection using timestamps and delivery IDs.
- Idempotency when the same delivery is retried.
- Fast acknowledgment with asynchronous processing.
- Minimal logging of sensitive email content.
Polling is still valuable as a fallback. Webhooks can be delayed by local network problems, queue backpressure, or deployment mistakes. A polling loop with a clear deadline can recover without turning the test into a fixed sleep.
Mailhook supports both real-time webhook notifications and polling APIs, so you can use webhook-first delivery and still provide deterministic fallback behavior for CI runners and agent tools.
6. Normalize email into JSON before agents touch it
LLM agents should not parse raw MIME, render arbitrary HTML, or decide which link to click based on the full email body. Email is untrusted input. It can contain tracking links, misleading anchor text, hidden HTML, prompt-injection text, and stale verification artifacts.
For agent workflows, set up a narrow extraction layer between the inbox and the model. The agent should receive a minimized view, not the whole message.
A safe agent-facing object might contain:
{
"inbox_id": "inb_123",
"message_id": "msg_456",
"received_at": "2026-05-11T21:11:07Z",
"artifact_type": "otp",
"otp": "123456",
"matched_purpose": "signup_verification"
}
For magic links, include the validated URL only after checking that the host, scheme, path pattern, and token shape match your expectations. Do not give an autonomous agent a list of every URL in the email and ask it to choose.
This is where structured JSON emails matter. Instead of scraping rendered inbox pages or brittle HTML, automation can read normalized fields, select by stable identifiers, and extract only the artifact needed for the next step.
7. Define lifecycle rules: TTLs, drain windows, and cleanup
A temp inbox should not live forever. Custom domains make addresses look more official, which makes lifecycle control even more important.
For each inbox type, decide how long it remains active, how late messages are handled, and when message content is removed or minimized. A CI signup verification inbox might need only minutes. A client operations workflow might need longer retention, depending on internal policy and the nature of the messages.
| Lifecycle setting | What it controls | Practical default |
|---|---|---|
| Active TTL | How long the inbox accepts expected messages | Match the test or workflow deadline |
| Drain window | How late arrivals are recorded after the main flow ends | Short window for debugging and dedupe |
| Tombstone | Minimal record after cleanup | Keep IDs and state, not full content |
| Retention policy | How long raw and normalized content is kept | Keep the minimum needed for debugging and compliance |
For high-volume workflows, batch processing can help process many received messages efficiently. The core rule remains the same: expire inboxes deliberately and avoid letting test addresses accumulate into unmanaged mailboxes.
8. Add observability before rollout
When custom-domain temp inboxes fail, the root cause can sit in DNS, the sender app, routing, webhook delivery, polling, parsing, or agent behavior. Observability makes those layers visible.
Log stable identifiers rather than full email content. At minimum, record the domain, inbox ID, generated email address, run ID, message ID, delivery ID, received timestamp, and extraction result. This lets you answer the most important debugging questions: Was the inbox created? Did DNS route the message? Did the webhook arrive? Did parsing find the expected artifact? Did the agent consume it once?
A healthy smoke test should confirm all of the following:
| Check | Success signal |
|---|---|
| DNS routing | MX records resolve to the expected provider targets |
| Inbox creation | API returns an address on the custom domain and an inbox ID |
| Message receipt | A test email appears in the expected inbox |
| JSON normalization | Subject, sender, body, and metadata are readable as structured data |
| Webhook delivery | The receiver verifies the signed payload and records the event |
| Polling fallback | The message can still be retrieved if webhook delivery is unavailable |
| Cleanup | The inbox expires or closes according to policy |
Do this in a non-critical environment first. Once the full path is proven, move one workflow at a time to the custom domain. Keep domain choice in configuration so you can fall back to a shared domain or alternate subdomain without rewriting agent logic.
Common mistakes to avoid
The first mistake is using your primary company domain. Even if it works, it creates unnecessary risk. Use a subdomain that can be routed, monitored, and rolled back independently.
The second mistake is treating the custom domain as the isolation boundary. It is not. Isolation comes from creating a separate inbox per attempt and reading from that inbox, not from dumping all messages into one domain-level mailbox.
The third mistake is exposing raw email to an LLM agent. Normalize the message, verify the webhook, extract the minimal artifact, and give the agent a constrained tool result.
The fourth mistake is skipping lifecycle. A temp inbox without expiration becomes a permanent mailbox with worse ergonomics. Define TTLs, drain windows, and retention rules at the same time you configure DNS.
How Mailhook fits the setup
Mailhook provides the primitives needed for this architecture: disposable inbox creation via API, structured JSON email output, RESTful API access, real-time webhooks, polling support, shared domains, custom domain support, signed payloads, and batch email processing.
That means your application, CI runner, or LLM agent can create a temporary inbox, use an address on the appropriate domain, receive the email as JSON, and process the result without logging into a mailbox or scraping an inbox UI.
For implementation details and agent-readable guidance, start with the Mailhook llms.txt reference. It is the right source to wire Mailhook into tools, agents, and automation code without guessing endpoint behavior.
Frequently Asked Questions
Do custom domains make temp inboxes more reliable? They can improve control, allowlisting compatibility, and environment separation, but reliability still depends on inbox isolation, deterministic waiting, webhook or polling behavior, and safe parsing.
Should I use my root domain for temporary inboxes? Usually no. Use a dedicated subdomain so MX changes and automation traffic do not interfere with employee mail or production customer email.
Do I need SPF, DKIM, and DMARC for a custom temp inbox domain? For inbound-only receipt, MX routing is the primary requirement. SPF, DKIM, and DMARC matter if the same domain sends email or if you are specifically testing sender authentication.
Can an LLM agent read the full email body? It can, but it usually should not. A safer pattern is to normalize the email into JSON, extract the OTP or verified magic link, and pass only that minimal artifact to the agent.
What should I test after adding the custom domain? Verify DNS MX resolution, create an inbox on the custom domain, send a smoke email, receive structured JSON, validate webhook signatures, confirm polling fallback, and expire the inbox successfully.
Build custom-domain temp inboxes without mailbox glue
If your agents or QA workflows need disposable inboxes on a domain you control, Mailhook gives you the core building blocks: API-created temp inboxes, JSON email delivery, real-time webhooks, polling fallback, signed payloads, and custom domain support.
Start with the Mailhook integration reference, then create your first inbox from Mailhook and move one email-dependent workflow from mailbox scraping to programmable JSON delivery.