If your test suite needs to customize email addresses, the hard part is not making addresses look realistic. The hard part is keeping every customized address routable, isolated, and easy for your automation to read.
That matters even more when the test actor is an AI agent or LLM. A human tester may notice that [email protected] and [email protected] both land in the same mailbox. An agent may not. It will keep waiting, poll the wrong inbox, or click a verification link from a previous run. The result looks like a flaky test, but the root cause is usually an email routing bug.
A reliable strategy separates two concerns:
- The address can be customized for the scenario, tenant, persona, branch, or run.
- The routing path must remain deterministic from send event to JSON payload.
Mailhook is built for that second part: disposable inboxes created via API, structured JSON email output, webhook notifications, polling, shared domains, custom domain support, signed payloads, and batch email processing. This guide focuses on how to design customized addresses so those capabilities stay predictable in QA, signup verification, and agent-driven workflows.
What “customizing” an email address should mean in tests
In production, email address customization often means branding, memorability, or user preference. In automated testing, it should mean traceability.
A customized test address should answer practical questions:
- Which test run created this inbox?
- Which scenario triggered the email?
- Which user role or tenant does it represent?
- Which system should receive and parse the message?
- Can the test retrieve the exact message without guessing?
The address is not just a string. It is part of your test contract.
At the SMTP level, email delivery is based on the mailbox format of local part plus domain, as defined in RFC 5321. In practical test systems, the domain determines where the message is routed, while the local part often determines which inbox, alias, or generated mailbox should receive it.
That means most routing bugs come from changing the wrong side of the address.
The routing rule that prevents most bugs
The simplest rule is this: customize the local part only when the domain is already configured to receive test mail, and never let an agent invent the domain.
For example, this is safe as a pattern:
qa-run-8421-signup@<mailhook-managed-domain>
qa-run-8421-reset@<mailhook-managed-domain>
qa-run-8421-admin@<mailhook-managed-domain>
This is risky:
[email protected]
[email protected]
[email protected]
The first pattern varies the local part while keeping routing anchored to a domain that your inbox system can receive. The second pattern allows the domain to drift. If the domain has no MX route, points to the wrong service, or overlaps with production infrastructure, your test will fail in ways that look unrelated to email.
If you are deciding which domains belong in which test environments, Mailhook’s guide to email domain names for testing is a useful companion because it separates validation-only domains from domains that must actually receive mail.
A safe address customization model
For most automated test suites, the safest model is to create one disposable inbox per test run, scenario, or user actor, then use an address format that encodes just enough context to debug failures.
A practical format looks like this:
<prefix>-<run-id>-<scenario>-<role>@<deliverable-test-domain>
For example:
qa-8421-signup-buyer@<deliverable-test-domain>
qa-8421-invite-admin@<deliverable-test-domain>
agent-8421-password-reset@<deliverable-test-domain>
The exact delimiter is less important than consistency. Use characters your application under test already accepts. Many real-world signup forms reject technically valid but uncommon email syntax, so prefer boring local parts: lowercase letters, digits, and hyphens.
Avoid making the address carry too much meaning. The test runner should know the inbox ID, scenario, and expected email type from its own state. The visible email address should help humans debug, not become the only source of truth.
| Address component | Good use | Routing risk to avoid |
|---|---|---|
| Prefix | Distinguish QA, CI, agent, or staging traffic | Using real employee names or production-like aliases |
| Run ID | Isolate parallel test runs | Reusing the same address across CI jobs |
| Scenario label | Make failures readable | Encoding long strings that forms may reject |
| Role label | Represent buyer, admin, invitee, or agent persona | Depending on role label to decide where mail is routed |
| Domain | Route to the inbox provider | Allowing tests or agents to invent domains |
This keeps your customization useful without making delivery fragile.
Do not confuse aliases with isolated inboxes
A common mistake is to customize addresses with aliases, then assume each alias behaves like a separate mailbox.
For example:
[email protected]
[email protected]
[email protected]
This can be fine for manual testing, but it is often dangerous in automation. Many systems normalize plus-addressed local parts. Some applications strip tags, lowercase unexpectedly, or treat aliases as the same account. Some email providers deliver every alias into one mailbox, which means your test runner has to filter messages after delivery.
Filtering is where flakiness begins. If two tests run in parallel and both wait for “the latest verification email,” one can easily consume the other’s message.
Disposable inboxes are safer because the inbox itself is the isolation boundary. Instead of customizing aliases inside one mailbox, create a distinct inbox through an API and bind the test to that inbox. Mailhook supports disposable inbox creation via API and returns received emails as structured JSON, which makes the inbox a programmable object rather than an overloaded shared mailbox.
For end-to-end suites that depend heavily on email, the broader pattern is covered in Mailhook’s article on how to create email on demand for end-to-end test suites.
Shared domains vs custom domains
There are two common ways to keep customized test addresses routable: use an instant shared domain from your inbox provider, or configure a custom domain dedicated to test traffic.
Shared domains are usually the fastest route for CI and agent experiments. You can generate inboxes without waiting on DNS changes, then receive emails via webhook or polling. This works well when the application under test does not require a branded sender-recipient domain relationship.
Custom domains are useful when your product or test environment needs addresses under a domain you control. They can also make test data easier to recognize in logs. The important rule is to isolate test traffic from production email. A dedicated subdomain, such as qa.example.com or mailtest.example.com, is usually safer than pointing a root domain or employee mail domain at test infrastructure.
| Option | Best for | Main caution |
|---|---|---|
| Instant shared domain | Fast CI runs, LLM-agent workflows, signup verification tests | Keep the generated domain immutable for the test |
| Custom test subdomain | Branded test flows, controlled routing, environment-specific mail | Configure DNS carefully and avoid production overlap |
| Production domain aliases | Rare controlled checks only | High risk of collisions, privacy issues, and misrouted mail |
If your team needs domain ownership in the test address, review the low-risk pattern for creating email addresses with a custom domain before connecting it to automated flows.

Give LLM agents an inbox descriptor, not just an address
LLM agents are good at adapting, but that adaptability is a liability when email routing must be exact. If you ask an agent to “use a test email address,” it may invent a plausible address that is not deliverable. It may also reuse an address from a prior tool call, change the domain to match a company name, or strip a suffix that looked unimportant.
Instead, give the agent a descriptor with immutables.
{
"emailAddress": "agent-8421-signup@<deliverable-test-domain>",
"inboxId": "<created-inbox-id>",
"routingDomainLocked": true,
"allowedUse": "signup verification for this run only",
"expectedMessage": "verification email"
}
The agent can use the emailAddress in the browser or API flow, but your harness should use the inboxId or equivalent internal reference to retrieve mail. This avoids a fragile pattern where the agent searches by address string or chooses which inbox to poll.
Mailhook also publishes implementation context for AI systems in its llms.txt, which is helpful when an agent or coding assistant needs to understand the product’s API-oriented email workflow without guessing capabilities.
Use webhook or polling logic that matches the test
Custom addresses prevent routing ambiguity, but your retrieval logic still needs to be deterministic.
Mailhook supports real-time webhook notifications and polling APIs for emails. Both can be reliable when used intentionally.
| Retrieval method | Use when | Reliability tip |
|---|---|---|
| Webhook | Your test infrastructure can receive callbacks | Verify signed payloads before trusting the email event |
| Polling | CI jobs cannot expose a callback URL or need simple control flow | Poll the specific inbox with a bounded timeout |
| Batch processing | A flow triggers multiple expected emails | Assert each expected message type instead of relying on arrival order |
A good test does not wait for “any email.” It waits for the email that matches the action it just triggered, in the inbox it just created, within a defined time budget.
For example, a signup verification test should know:
Created inbox: agent-8421-signup@<deliverable-test-domain>
Triggered action: user signup
Expected message: verification or confirmation email
Retrieval path: webhook event or polling against the created inbox
Timeout: bounded and explicit
Assertion: parse structured JSON and validate the expected link or token
Structured JSON output is especially useful here. Instead of scraping a raw mailbox UI, your test can inspect fields in a machine-readable payload. That is better for deterministic QA and much better for LLM agents, which should operate on explicit data whenever possible.
Common routing bugs when teams customize email addresses
Most email-related flakes are not random. They come from repeatable design mistakes.
Bug 1: The domain changes during the test
The test creates one address, but the app, agent, or fixture transforms it before submission. This can happen when a helper builds email addresses independently in multiple places.
Prevent it by creating the inbox once, storing the resulting address as a test fixture value, and passing that exact value to every actor. Do not reconstruct it later.
Bug 2: The local part is unique, but the inbox is not
Teams often append a run ID to an alias while still routing all messages into one mailbox. That gives the appearance of uniqueness without true isolation.
Prevent it by making inbox creation part of the setup step. The test should not only generate a string. It should create or reserve a mailbox that the receiving API can query directly.
Bug 3: Parallel CI jobs reuse a readable label
Addresses like signup-test@... or qa-user@... are easy to read, but they collide as soon as tests run concurrently.
Prevent it by including a run-specific token in the local part. The token can be a CI build ID, timestamp plus random suffix, or another unique identifier controlled by your test runner.
Bug 4: Custom domains overlap with production mail
A custom domain can make tests cleaner, but only if it is isolated. Routing test mail through a production mailbox or employee domain introduces privacy and reliability risks.
Prevent it by using a dedicated test subdomain and keeping DNS ownership clear. Test mail should never depend on a human inbox.
Bug 5: The agent waits on the wrong condition
An LLM agent may decide that an email “probably arrived” because it sees a success page, or it may search for a generic subject line across previous messages.
Prevent it by keeping email retrieval outside the agent’s free-form reasoning. Let the harness wait for the message in the correct inbox, then pass only the relevant structured result back to the agent.
A practical checklist for customized test email addresses
Before you roll a new address pattern into CI or agent workflows, check the following:
- The domain is deliverable and controlled by your test inbox system.
- Each parallel run gets a unique inbox or unique retrieval boundary.
- The local part uses simple characters accepted by your app under test.
- The address is created before the application sends email to it.
- The test retrieves messages from the created inbox, not a shared mailbox search.
- Webhook payloads are verified if callbacks are used.
- Polling has a clear timeout and does not query unrelated inboxes.
- LLM agents receive the address as an immutable value, not as a prompt suggestion.
This checklist may look strict, but it removes a large class of failures that otherwise appear as intermittent CI instability.
Frequently Asked Questions
What is the safest way to customize email addresses for automated tests? Create a disposable inbox through an API, then use a unique local part that includes a run identifier while keeping the domain fixed to a deliverable test domain.
Should I use plus addressing for test emails? Plus addressing can work in some systems, but it is risky for automation because applications and providers may normalize it. Isolated disposable inboxes are usually more reliable.
Can LLM agents create their own test email addresses? They should not invent addresses freely. A test harness should create the inbox, pass the exact email address to the agent, and keep retrieval tied to the created inbox ID or descriptor.
When should I use a custom domain for test email? Use a custom test domain or subdomain when your workflow needs domain ownership, branding, or environment-specific routing. Keep it separate from production email infrastructure.
How do webhooks help reduce email test flakiness? Webhooks let your system react when an email arrives instead of repeatedly guessing. For security, verify signed payloads before treating a webhook event as valid.
Build routable test inboxes instead of fragile aliases
Customizing test email addresses should make automation easier to trace, not harder to route. The safest pattern is to create disposable inboxes programmatically, lock the routing domain, encode only useful context in the local part, and retrieve messages as structured data.
With Mailhook, teams can create disposable inboxes via API, receive emails as JSON, use webhooks or polling, work with shared or custom domains, and support LLM-agent workflows without relying on shared mailbox hacks. No credit card is required to get started.