Combine Pydantic AI's type safety and dependency injection with MultiMail's email infrastructure for validated, reliable email agents with human oversight.
Pydantic AI is an agent framework built on Pydantic that brings type safety and validation to LLM interactions. It uses dependency injection for tools and provides structured, validated outputs from agents. MultiMail provides the email infrastructure that Pydantic AI agents need to actually send, receive, and manage messages with production reliability.
By integrating MultiMail with Pydantic AI, you get type-safe email operations where tool inputs and outputs are validated by Pydantic models. The dependency injection pattern makes it clean to pass MultiMail API credentials and configuration to your tools without global state. MultiMail's gated_send mode adds human judgment on top of type validation.
Connect Pydantic AI to MultiMail by defining tool functions with type-annotated parameters and using dependency injection to provide API configuration. The framework validates all inputs and outputs automatically.
Pydantic AI validates tool inputs with Pydantic models. Define typed schemas for email recipients, subjects, and bodies, ensuring your agent always sends well-structured API requests to MultiMail.
Pass MultiMail API keys, mailbox IDs, and configuration through Pydantic AI's dependency injection system. No global state or environment variable hacks — clean, testable configuration management.
Pydantic AI's result validators ensure structured output, but cannot prevent a well-structured but inappropriate email. MultiMail's oversight adds human judgment at the delivery layer, catching what validators miss.
Define result types for email operations — classification results, draft summaries, delivery status — all validated by Pydantic models. Your application code gets typed objects, not raw strings.
Pydantic AI's dependency injection makes it easy to test email agents with mock MultiMail responses. Swap the real API client for a test double without changing agent logic.
import requests
from dataclasses import dataclass
from pydantic import BaseModel
from pydantic_ai import Agent, RunContext
MULTIMAIL_API = "https://api.multimail.dev/v1"
@dataclass
class EmailDeps:
api_key: str
mailbox_id: str
@property
def headers(self) -> dict:
return {"Authorization": f"Bearer {self.api_key}"}
class EmailResult(BaseModel):
summary: str
actions_taken: list[str]
agent = Agent(
"openai:gpt-4o",
deps_type=EmailDeps,
result_type=EmailResult,
system_prompt="You are an email assistant using MultiMail. "
"The mailbox uses gated_send mode — sent emails require "
"human approval before delivery."
)Set up the dependency injection context and MultiMail tools with type-safe parameters.
@agent.tool
def send_email(ctx: RunContext[EmailDeps], to: str, subject: str, body: str) -> str:
"""Send an email through MultiMail. In gated_send mode, queues for approval."""
resp = requests.post(f"{MULTIMAIL_API}/send", headers=ctx.deps.headers, json={
"mailbox_id": ctx.deps.mailbox_id,
"to": to, "subject": subject, "body": body
})
return str(resp.json())
@agent.tool
def check_inbox(ctx: RunContext[EmailDeps], limit: int = 10) -> str:
"""Check inbox for recent messages."""
resp = requests.get(
f"{MULTIMAIL_API}/mailboxes/{ctx.deps.mailbox_id}/inbox",
headers=ctx.deps.headers, params={"limit": limit}
)
return str(resp.json())
@agent.tool
def reply_email(ctx: RunContext[EmailDeps], message_id: str, body: str) -> str:
"""Reply to an email by message ID, maintaining thread context."""
resp = requests.post(f"{MULTIMAIL_API}/reply", headers=ctx.deps.headers, json={
"message_id": message_id, "body": body
})
return str(resp.json())
@agent.tool
def get_thread(ctx: RunContext[EmailDeps], thread_id: str) -> str:
"""Get all messages in an email thread."""
resp = requests.get(
f"{MULTIMAIL_API}/threads/{thread_id}",
headers=ctx.deps.headers
)
return str(resp.json())Add MultiMail tools to the agent using dependency injection for API configuration.
async def main():
deps = EmailDeps(
api_key="mm_live_your_api_key",
mailbox_id="your_mailbox_id"
)
result = await agent.run(
"Check my inbox and summarize unread messages. "
"Reply to any urgent emails with an acknowledgment.",
deps=deps
)
"cm"># result.data is a validated EmailResult
print(f"Summary: {result.data.summary}")
print(f"Actions: {result.data.actions_taken}")
import asyncio
asyncio.run(main())Execute the email agent with typed dependencies and get a structured result.
from pydantic_ai import Agent, RunContext, ModelRetry
@agent.result_validator
async def validate_email_result(
ctx: RunContext[EmailDeps], result: EmailResult
) -> EmailResult:
"""Validate the agent&"cm">#039;s output before returning."""
if not result.summary:
raise ModelRetry("Please provide a summary of what you found.")
if len(result.summary) < 20:
raise ModelRetry("Summary is too brief. Include key details.")
return result
"cm"># The validator ensures the agent retries if output quality is low.
"cm"># MultiMail's gated_send mode separately ensures email delivery
"cm"># requires human approval — two layers of quality control.Use Pydantic AI's result validators to check email agent output quality.
Sign up at multimail.dev, create a mailbox, and generate an API key. Your key will start with mm_live_.
Install Pydantic AI and requests for calling the MultiMail API.
pip install pydantic-ai requestsCreate a dataclass for email dependencies (API key, mailbox ID) and a Pydantic model for structured results.
Decorate functions with @agent.tool that accept RunContext[EmailDeps] and call MultiMail API endpoints with the injected configuration.
Execute the agent with your dependencies. Review pending emails in the MultiMail dashboard when using gated_send mode.
result = await agent.run("Check my inbox", deps=deps)Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 38-tool MCP server. Formally verified in Lean 4.