The OpenAI Agents SDK is a lightweight, open-source framework for building agentic applications with OpenAI models. It provides a clean abstraction for defining agents, tools, and handoffs. With AgentSend, you can extend any OpenAI agent with email capabilities using the SDK's @function_tool decorator, giving your agent a real email address and the ability to send, receive, and manage conversations.
This guide walks through defining email function tools for the OpenAI Agents SDK, wiring them into an agent, and handling incoming email through webhooks. The same pattern works whether you are using GPT-4o, o3, or any other model supported by the SDK.
Why OpenAI Agents Need Email
The OpenAI Agents SDK makes it easy to build agents that use tools, follow instructions, and hand off tasks to other agents. But when those agents need to communicate with humans outside your application, they need an external channel. Email is the most universal option:
- Async communication: Unlike chat, email does not require both parties to be online simultaneously. Your agent can send a message and process the reply hours or days later.
- Professional context: For business use cases like customer support, vendor management, or HR, email is the expected communication channel.
- Audit trail: Email provides a built-in record of all agent-human interactions, which is critical for compliance and debugging.
- Universal reach: Every business contact has an email address. Your agent can communicate with anyone without requiring them to install an app or visit a website.
Setting Up
Sign up at agentsend.io to get an API key and create an inbox. Then install the dependencies:
pip install openai-agents requests
Defining Email Function Tools
The OpenAI Agents SDK uses the @function_tool decorator to turn regular Python functions into tools that agents can invoke. Here is a complete implementation of send and check-inbox tools using AgentSend:
import os import requests from typing import Optional from agents import function_tool AGENTSEND_API_KEY = os.environ["AGENTSEND_API_KEY"] AGENTSEND_BASE = "https://agentsend.io" @function_tool def send_email( to: str, subject: str, body: str, inbox_id: str, thread_id: Optional[str] = None, ) -> str: """Send an email via AgentSend. Args: to: Recipient email address. subject: Email subject line. body: Plain text email body. inbox_id: AgentSend inbox ID to send from. thread_id: Thread ID for replies (omit for new conversations). """ payload = { "inboxId": inbox_id, "to": to, "subject": subject, "text": body, } if thread_id: payload["threadId"] = thread_id resp = requests.post( f"{AGENTSEND_BASE}/messages", json=payload, headers={"Authorization": f"Bearer {AGENTSEND_API_KEY}"}, ) resp.raise_for_status() data = resp.json() return f"Email sent. Message ID: {data['id']}, Thread: {data['threadId']}" @function_tool def check_inbox(inbox_id: str) -> str: """Check for new messages in an AgentSend inbox. Args: inbox_id: The AgentSend inbox ID to check for new messages. """ resp = requests.get( f"{AGENTSEND_BASE}/inboxes/{inbox_id}/messages", headers={"Authorization": f"Bearer {AGENTSEND_API_KEY}"}, ) resp.raise_for_status() messages = resp.json().get("messages", []) if not messages: return "No new messages." lines = [] for msg in messages[:5]: lines.append( f"From: {msg['from']}, Subject: {msg['subject']}, " f"Thread: {msg['threadId']}" ) return "\n".join(lines) @function_tool def get_thread(thread_id: str) -> str: """Get all messages in an email thread. Args: thread_id: The thread ID to retrieve conversation history for. """ resp = requests.get( f"{AGENTSEND_BASE}/threads/{thread_id}/messages", headers={"Authorization": f"Bearer {AGENTSEND_API_KEY}"}, ) resp.raise_for_status() data = resp.json() lines = [f"Thread: {data['subject']}"] for msg in data["messages"]: lines.append(f"[{msg['from']}]: {msg['text']}") return "\n".join(lines)
The @function_tool decorator automatically generates the JSON schema for each function from its type hints and docstring. The agent sees the tool descriptions and can invoke them during its execution loop.
Wiring Tools into an Agent
With the tools defined, create an agent that uses them:
import asyncio from agents import Agent, Runner from email_tools import send_email, check_inbox, get_thread email_agent = Agent( name="Email Assistant", instructions=( "You are a helpful assistant with email capabilities. " "Your inbox ID is inbox_abc123. You can send emails, " "check for new messages, and read thread history. " "Always use thread_id when replying to keep conversations grouped." ), tools=[send_email, check_inbox, get_thread], ) async def main(): result = await Runner.run( email_agent, "Check my inbox for new messages and summarize them.", ) print(result.final_output) asyncio.run(main())
When executed, the agent will call check_inbox with the configured inbox ID, process the results, and return a human-readable summary. If you ask it to reply to a message, it will compose an appropriate response and call send_email with the correct thread ID.
Tip: The OpenAI Agents SDK supports agent handoffs. You can create a specialized "email agent" and have your main agent hand off to it whenever email communication is needed, keeping concerns cleanly separated.
Agent Handoffs for Email
One powerful pattern with the OpenAI Agents SDK is using handoffs to delegate email tasks to a specialized agent. Your primary agent handles the core logic, and when it needs to communicate via email, it hands off to the email agent:
from agents import Agent email_agent = Agent( name="Email Agent", instructions="You handle all email communication. " "Inbox ID: inbox_abc123.", tools=[send_email, check_inbox, get_thread], ) main_agent = Agent( name="Support Manager", instructions=( "You manage customer support. When you need to " "send or check email, hand off to the Email Agent." ), handoffs=[email_agent], )
This pattern keeps your main agent focused on decision-making while the email agent handles the mechanics of sending and receiving messages. The SDK manages the handoff seamlessly.
Handling Incoming Email
Set up an AgentSend webhook to receive incoming emails in real time. When a message arrives, your webhook handler can trigger the agent to process and respond:
from fastapi import FastAPI, Request from agents import Runner app = FastAPI() @app.post("/webhook/email") async def handle_email(request: Request): payload = await request.json() msg = payload["message"] result = await Runner.run( email_agent, f"New email from {msg['from']}. " f"Subject: {msg['subject']}. " f"Body: {msg['text']}. " f"Thread: {msg['threadId']}. " f"Reply appropriately.", ) return {"status": "processed", "output": result.final_output}
Next Steps
- Add guardrails: Use the SDK's guardrails feature to validate outgoing emails before they are sent, checking for tone, accuracy, or sensitive information.
- Implement tracing: The SDK's built-in tracing captures every tool call, making it easy to audit which emails your agent sent and why.
- Use streaming: For long email compositions, use the SDK's streaming support to show progress in real time.
- Connect custom domains: Send from professional addresses like agent@yourdomain.com instead of @agentsend.io addresses.
Frequently Asked Questions
Can OpenAI Agents SDK agents send email?
Yes. By defining a function tool with the @function_tool decorator that calls AgentSend's REST API, any OpenAI Agents SDK agent can send email, check inboxes, and manage threaded conversations. The tool is invoked automatically by the agent during its execution loop.
How do I define an email function tool for OpenAI Agents?
Use the @function_tool decorator from the openai-agents SDK to define a Python function that calls AgentSend's /messages endpoint. The decorator automatically generates the function schema from the type hints and docstring, making it available to the agent.
Does AgentSend work with OpenAI Assistants API?
Yes. While this guide focuses on the OpenAI Agents SDK (the newer open-source framework), AgentSend also works with the Assistants API. Define an email function in your assistant's tools array, and handle the function call by forwarding requests to AgentSend's API.
Give Your OpenAI Agent an Email Address
Free tier available. No credit card required. Add email to your OpenAI agent in under 5 minutes.
Start for Free →