Getting an email address on your own domain is easy. Getting one that is reliable for automation and AI agents (and does not involve logging into a human mailbox) is where teams usually lose time.
This fast setup guide shows a pragmatic path to create email with your own domain for inbound flows like signup verification, OTPs, password resets, and integration testing, with a minimal, automation-friendly architecture.
What you are actually setting up (inbound email, not a “mailbox account”)
When people say “create an email with your own domain,” they often mean one of two things:
- Human mailbox accounts (Google Workspace, Microsoft 365, IMAP, login, folders)
- Programmable inbound email (receive emails as machine-readable events, parse them as JSON, trigger workflows)
For LLM agents, CI, and QA automation, the second is usually the goal: you want an address on your domain that can be created on demand, isolated per run, and consumed deterministically.
A minimal inbound architecture looks like this:
- DNS routes mail for a domain or subdomain using MX records.
- An inbound provider receives SMTP and normalizes the email.
- Your code consumes the message via webhooks (push) and/or polling (pull).

If you want the exact integration contract for Mailhook (endpoints, payload shape, signature verification details), use the canonical reference: Mailhook llms.txt.
Fast decision: domain vs subdomain (pick subdomain by default)
For automation, a dedicated subdomain keeps your risk and blast radius small. It also lets you keep your human email (like @company.com) untouched.
Examples:
| Use case | Recommended inbound domain | Why it works well |
|---|---|---|
| CI / automated QA | ci-mail.example.com |
Easy to rotate and isolate, safe to delete later |
| Staging | staging-mail.example.com |
Separates deliverability and routing from production |
| Agent workflows | agent-mail.example.com |
Keeps “tooling email” away from human mail |
| Production verification (controlled) | verify.example.com |
Lets you apply allowlists and monitoring to one zone |
If you are unsure, start with one subdomain (for example ci-mail.example.com) and expand later.
Step 1: Choose how you will “receive” email in code
You have three realistic choices:
| Approach | Setup speed | Reliability for CI/agents | Operational cost | Best for |
|---|---|---|---|---|
| Self-host inbound SMTP + parser | Slow | Medium (often brittle) | High | Specialized pipelines, deep control |
| Traditional mailbox provider | Medium | Low in parallel CI | Medium | Humans, not automation |
| Inbound email API (JSON + webhooks) | Fast | High | Low to Medium | Automation, agents, verification flows |
If your goal is automation, inbound email API is typically the fastest route because you avoid running SMTP infrastructure and MIME parsing.
Mailhook is built specifically for this automation-first model (disposable inbox creation via API, emails as structured JSON, webhook notifications, polling API fallback, signed payloads, batch processing, shared domains and custom domain support). See Mailhook for the product overview.
Step 2: Decide your addressing scheme (how you generate recipients)
After MX routes mail to your inbound provider, you still need a consistent strategy for generating unique recipient addresses.
You will usually pick one of these patterns:
| Pattern | Example recipient | State required | Scales in parallel CI | Notes |
|---|---|---|---|---|
| Encoded local-part | [email protected] |
No | Yes | Great for determinism and correlation |
| Alias table | [email protected] |
Yes | Medium | Needs lifecycle/cleanup of aliases |
| Catch-all + tag | [email protected] |
Depends | Medium | Some senders strip tags or normalize addresses |
For LLM agents and CI runs, encoded local-parts are usually the simplest, because you can generate a correlation token without coordinating shared state.
Design tip: keep tokens URL-safe and email-safe (letters, digits, a few separators), and treat the whole email address as an identifier, not a user.
Step 3: Configure DNS MX for your subdomain
MX records tell the world where to deliver email for a domain. This is the point where “I created an email address string” becomes “mail is actually routable.”
High-level checklist:
- Create the subdomain you picked (for example
ci-mail.example.com). - Add the MX records your inbound provider requires.
- Wait for DNS propagation.
Most DNS providers have a UI to add an MX record. If you prefer to verify from the command line, you can use dig:
# Replace with your subdomain
DIG_DOMAIN="ci-mail.example.com"
dig MX "$DIG_DOMAIN" +short
What you are looking for is that the MX answers match what your provider expects.
Two common time sinks:
-
Editing the wrong zone (editing
example.comwhen you meantci-mail.example.com, or vice versa) - Assuming it is instant (DNS can take minutes to hours depending on TTL and propagation)
For an accessible MX primer, Cloudflare’s overview is a solid reference: MX records explained.
Step 4: Connect your inbound provider and run a one-email smoke test
Once MX is correct, do a single end-to-end test before you automate anything:
- Generate a unique recipient on your subdomain.
- Send one email to it.
- Confirm you can retrieve the message in code.
If you are using Mailhook with a custom domain, rely on the canonical docs for the exact steps, payload fields, and signature verification requirements: Mailhook llms.txt.
What “good” looks like for automation
A good provider workflow for tests and agents usually includes:
- Create a disposable inbox via API
- Receive emails as structured JSON
- Get real-time delivery via webhook
- Keep polling as a fallback for reliability
- Verify authenticity with signed payloads
Those capabilities map directly to Mailhook’s feature set (disposable inboxes, JSON output, webhook notifications, polling API, signed payloads, batch processing, shared domains, custom domain support).
Step 5: Implement the minimal “inbox tool” your agent or test harness needs
Whether you are building an LLM tool or a QA helper library, keep the interface tiny and deterministic. A practical contract is:
createInbox({ domain }) -> { email, inbox_id, expires_at }waitForEmail({ inbox_id, matcher, deadline }) -> { message_json }extractArtifact({ message_json }) -> { otp | verification_url }expireInbox({ inbox_id })
You do not need to expose full HTML to the agent. In fact, you usually should not.
Here is provider-agnostic pseudocode that shows the shape without inventing any Mailhook endpoints:
// Pseudocode: consult your provider docs for exact endpoints and fields.
const inbox = await createInbox({ domain: "ci-mail.example.com", ttlSeconds: 900 })
// inbox.email might look like: "[email protected]"
await triggerSignup({ email: inbox.email })
const message = await waitForEmail({
inboxId: inbox.inbox_id,
deadlineMs: 60_000,
match: {
subjectIncludes: "Verify your email",
fromDomain: "your-app.example",
}
})
const artifact = extractVerificationArtifact(message)
await completeVerification(artifact)
await expireInbox({ inboxId: inbox.inbox_id })
Why this is agent-friendly
- Isolation: inbox-per-run (or inbox-per-attempt) prevents collisions.
- Determinism: explicit deadline beats hard sleeps.
- Safety: the agent sees only the minimum artifact it needs.
Step 6: Use webhooks first, keep polling as a safety net
Webhooks are the fastest way to make your tests and agents responsive, but polling is still valuable for resilience.
A reliable hybrid posture:
- Webhook handler writes the message into your datastore or queue.
- If the webhook does not arrive by a deadline, your harness polls the inbox.
Webhook security essentials (do these even in test environments)
If you consume inbound email as JSON, your webhook endpoint becomes an attack surface. Treat it like any other public API.
At minimum:
- Verify signed payloads using the provider’s documented algorithm.
- Verify the signature over the raw request body (before parsing JSON).
- Add replay protection, for example rejecting repeated delivery IDs.
- Fail closed, if verification fails, do not process.
Mailhook supports signed payloads, and the exact verification details should come from the canonical integration reference: Mailhook llms.txt.
Step 7: Avoid the top 5 “why didn’t my email arrive?” problems
These are the issues that most often cause delays during a “fast setup.”
1) Confusing envelope recipient with the To: header
SMTP routing is based on the envelope recipient (RCPT TO), which may not match the To: header you see in the email body. If your system relies on the header only, you will misroute forwarded or BCC scenarios.
If you want the standards context, RFC 5321 defines the SMTP transaction semantics: RFC 5321.
2) MX added, but to the wrong host name
A classic mistake is adding MX for example.com when you meant ci-mail.example.com, or vice versa.
3) DNS propagation not complete
Your local resolver might show the new MX, but other resolvers still have the old value cached.
4) Catch-all collisions
If you use a catch-all domain without a strong correlation token, parallel tests can read each other’s messages.
5) Over-parsing HTML
For automation, prefer stable, minimal extraction from text content and structured fields. HTML is a rendering format, it is not a contract.
Step 8: Add two guardrails that save hours later (retention and logging)
Even if your goal is “fast,” add these two practices early:
Retention and cleanup
- Use short TTLs for disposable inboxes.
- Expire inboxes when you are done.
- Keep only what you need for debugging (often the normalized JSON and a message ID).
Mailhook supports disposable inbox lifecycle patterns and batch processing, which helps when you create many inboxes for CI and agent workloads.
Log IDs, not entire emails
For debuggability without leaking secrets:
- Log
inbox_id, message IDs, timestamps, and matcher decisions. - Avoid logging full bodies unless you are in a controlled, redacted environment.
Putting it together with Mailhook (quick start mindset)
If you want to move fast without running SMTP infrastructure, Mailhook is designed for the exact workflows this guide targets:
- Create disposable inboxes via API
- Receive email as structured JSON
- Get webhook notifications in real time
- Use polling as a fallback
- Verify signed payloads
- Support shared domains for instant start and custom domains when you need allowlisting or brand control
To implement against the real contract (instead of guessing), start here: Mailhook llms.txt. Then, when you are ready to try it, go to Mailhook (no credit card required) and wire your first custom-domain inbox end to end.