Email for an OpenAI Agents SDK agent
Give the agent its own inbox in one call, then hand it three function tools: send, list, read. About 40 lines of Python in total.
To give an OpenAI Agents SDK agent email, register an address with one POST to Mailgi, then expose three plain Python functions as function_tool: send_email, list_inbox and read_email. The agent can then receive verification codes, answer mail and write to people from its own address, with no OAuth and no shared human inbox.
Give the agent an address
One call, no signup form. Do this once per agent and keep the key in your secrets store.
Register
The response carries the address and an API key that is shown exactly once.
Wait a couple of seconds before the first send while the mailbox is provisioned.
# Once, outside the agent: give it an address and keep the key
curl -s -X POST https://api.mailgi.xyz/v1/agents/register \
-H "Content-Type: application/json" -d '{"label":"my-agent"}'
# => {"emailAddress":"buzzing-falcon@mailgi.xyz","apiKey":"amb_..."}
export MAILGI_API_KEY=amb_... # shown once: store it
Three tools, plain HTTP
Framework-free functions over the REST API, saved as mailgi_tools.py. Each was run against the live API before publishing.
The three tools
Docstrings become the tool descriptions the model sees, so keep them short and literal.
from is a list of objects; the address is m['from'][0]['email']. Treating it as a string is the most common mistake with this API.
A 402 comes back as text with a top-up link rather than an exception, so the model can explain it.
import os, time, requests
API = "https://api.mailgi.xyz"
KEY = os.environ["MAILGI_API_KEY"]
H = {"Authorization": f"Bearer {KEY}"}
def send_email(to: str, subject: str, body: str) -> str:
"""Send a plain-text email from the agent's own address."""
r = requests.post(f"{API}/v1/mail/send", headers=H,
json={"to": [to], "subject": subject, "textBody": body})
if r.status_code == 402:
return "Not sent: balance too low. Top up: " + r.json()["error"]["details"]["topupPage"]
r.raise_for_status()
return f"Sent, message id {r.json()['messageId']}"
def list_inbox(limit: int = 10) -> str:
"""List the newest messages: id, sender, subject, preview."""
r = requests.get(f"{API}/v1/mail", headers=H, params={"limit": limit})
r.raise_for_status()
return "\n".join(
f"{m['id']} | {m['from'][0]['email']} | {m['subject']} | {m['preview'][:80]}"
for m in r.json()["messages"]) or "Inbox is empty."
def read_email(message_id: str) -> str:
"""Read one message's full text."""
r = requests.get(f"{API}/v1/mail/{message_id}", headers=H)
r.raise_for_status()
m = r.json()
body = m.get("textBody") or m.get("htmlBody") or ""
if isinstance(body, list): # JMAP parts
body = "".join(m["bodyValues"][p["partId"]]["value"] for p in body)
return f"From: {m['from'][0]['email']}\nSubject: {m['subject']}\n\n{body}"
Wire them into OpenAI Agents SDK
The agent
function_tool reads the type hints and docstring to build each tool's schema; the three functions need no SDK-specific code.
Tested with openai-agents 0.24. Uses whichever model your Agents SDK is configured for.
# pip install openai-agents requests
from agents import Agent, Runner, function_tool
import mailgi_tools as mail
agent = Agent(
name="Mail agent",
instructions=(
"You have your own email address. Use list_inbox and read_email "
"to check mail, and send_email to reply. Never invent a message id."
),
tools=[function_tool(mail.send_email),
function_tool(mail.list_inbox),
function_tool(mail.read_email)],
)
result = Runner.run_sync(agent, "Check my inbox and summarise anything new.")
print(result.final_output)
What the tools can't do yet
Better to know before the model tries.
No search, no threads
There is no search parameter; list_inbox pages newest first and the model matches messages itself. Replies are new messages with “Re:” in the subject.
No attachments
Send links instead. Incoming attachments are not exposed.
Mail to strangers is metered
Sending to other @mailgi.xyz agents is free; to outside addresses it costs $0.0005 per recipient plus $0.02 the first time you write to a new address. With no balance, send_email returns the top-up link instead of raising, so the model can tell the user.
Questions, answered
01How do I give an OpenAI agent an email address?
Register one with a single POST to https://api.mailgi.xyz/v1/agents/register, store the API key it returns, and give the agent tools that call the Mailgi REST API to send, list and read mail. No OAuth or human signup is needed.
02Can an OpenAI Agents SDK agent receive verification codes?
Yes. Once it has its own address it can use list_inbox and read_email to find the code. Poll every few seconds after triggering the signup; most codes arrive within seconds.
03Is there an MCP server instead of function tools?
Yes, at https://api.mailgi.xyz/mcp. Function tools are simpler when you only need send, list and read; the MCP server exposes the whole API.
04What does it cost?
Registering and receiving are free, and so is mail between Mailgi agents. Mail to outside addresses costs $0.0005 per recipient plus $0.02 the first time an agent writes to a new address, from a prepaid balance.
One POST to start.
No signup form, no OAuth, no card to start. Receiving is free.
Open the dashboard →