OpenAI's Agents SDK (formerly Swarm) provides a clean interface for defining tools that agents can call. You define a Python function, decorate it, and the SDK handles the function-calling JSON schema automatically. The missing piece for most agent pipelines is email — receiving OTPs, reading verification links, and sending replies.
Installing the Dependencies
pip install openai-agents agentmailr
Defining Email Tools
from agents import Agent, Runner, function_tool
from agentmailr import AgentMailr
client = AgentMailr(api_key="ak_your_key")
@function_tool
def create_inbox() -> dict:
"""Create a new disposable email inbox and return its address."""
inbox = client.inboxes.create()
return {"inbox_id": inbox.id, "address": inbox.address}
@function_tool
def wait_for_otp(inbox_id: str, timeout: int = 60) -> dict:
"""Wait up to timeout seconds for an OTP email and return the code."""
result = client.inboxes.get_otp(inbox_id, timeout=timeout)
return {"otp": result.otp_code}
@function_tool
def read_latest_email(inbox_id: str) -> dict:
"""Return the subject, sender, and text body of the latest email."""
emails = client.inboxes.list_emails(inbox_id, limit=1)
if not emails:
return {"error": "no emails yet"}
e = emails[0]
return {"subject": e.subject, "from": e.from_, "body": e.body_text}
agent = Agent(
name="SignupAgent",
instructions="You sign up for services and verify email addresses.",
tools=[create_inbox, wait_for_otp, read_latest_email],
)
result = Runner.run_sync(agent, "Sign up for a free account at app.example.com and verify the email.")
print(result.final_output)
Why Long-Polling Beats Periodic Tool Calls
A naive implementation would have the agent call read_latest_email repeatedly until an email arrives. This wastes tokens, burns through rate limits, and adds latency. AgentMailr's get_otp endpoint holds the HTTP connection open server-side until the email arrives, then returns immediately — no polling, no wasted calls, deterministic timing.
Parallel Agents, Zero Collisions
Each agent instance calls create_inbox to get its own unique address. Even if you run 1,000 parallel signup agents, every inbox is isolated. There are no race conditions, no shared state, no email collisions. Each agent waits on its own address and receives exactly its own emails.