Recipe: let an agent sign itself up for a service
The full pattern for an autonomous signup: one labelled inbox per service, a real verification code, an audit trail, and the rules about where not to point it.
How it fits together
An agent that can receive mail can finish the one step that used to need a human: the verification code. The mechanics take about ten lines. What takes longer is deciding which address belongs to which service, how you find out afterwards what the agent signed up for, and which sites you should not point it at in the first place — so this page covers those too.
One inbox per service, labelled with the service
The label is the audit trail. A single shared inbox saves nothing and makes every later question — who sent this, which account is this — unanswerable.
from mailflat import MailFlat
mf = MailFlat() # reads MAILFLAT_API_KEY
def inbox_for(service: str):
"""One permanent address per service, named after it."""
return mf.create(
prefix=service.replace(".", "-"),
label=f"agent/{service}",
retention_hours=24,
)
inbox = inbox_for("staging.example.com")
print(inbox.address) # staging-example-com-8f3@x7k2m.mailflat.net
The address stays until you delete it — only the messages inside expire. That is what makes a password reset six weeks later still possible.
Sign up, then read the real code
The browser calls are whatever driver you already use. The point is that no test-mode backdoor is involved: the service sends a real email and the agent reads it.
browser.goto("https://staging.example.com/signup")
browser.fill("#email", inbox.address)
browser.click("#submit")
code = inbox.wait_for_otp(timeout=60)
browser.fill("#code", code)
browser.click("#verify")
Magic links, when there is no code
Same shape, one step shorter: take the first link out of the message and follow it. links is extracted for you, in order of appearance.
msg = inbox.wait_for_message(timeout=60)
verify_url = msg.raw["links"][0]
browser.goto(verify_url)
Ask later what it signed up for
Labels turn into a report. This is the question you cannot answer if the agent used one shared address for everything.
curl -s https://mailflat.net/api/v1/inboxes \
-H "X-API-Key: $MAILFLAT_API_KEY" \
| grep -o '"label":"agent/[^"]*"'
Close the account, then the inbox
Delete the inbox last. Once it is gone you cannot receive the confirmation mail the service sends when you close an account.
# 1. cancel the account through the service itself
# 2. read the confirmation it sends
mf.inbox(inbox.address).wait_for_message(timeout=60)
# 3. only now let the address go
mf.inbox(inbox.address).delete()
Worth knowing
Point an agent only at services you are allowed to automate — your own staging, or a site whose terms permit it. Ours are at mailflat.net/legal/acceptable-use; a service that forbids automated signups is not made acceptable by an agent doing the typing.
Give the agent its own API key and nothing else. The key can list and delete every inbox it can see, so a key scoped to agent work is the difference between a bad run costing one inbox and costing all of them.
Encrypted inboxes cannot yield a code to any client, including your own agent — the server never sees the plaintext. Keep encryption for mail you read yourself and leave agent inboxes unencrypted.
Set retention_hours to the life of the task, not the maximum your plan allows. A 24-hour window is enough to finish a signup and short enough that a forgotten inbox empties itself.
See also
MCP server
Run the MailFlat MCP server and any Model Context Protocol client gains real inbox tools: open an address, wait for the one-time code, send mail, clean up.
Agents & Testing tabsWhich mode to use for API-created inboxes versus throwaway test inboxes, and how to audit what an automated agent did.
One-time codesMailFlat pulls the verification code out of the message for you and hands it over as a field, so your test never runs a regex against an email body.