Type-Safe Email Agents with Pydantic AI

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.

Built for Pydantic AI developers

Type-Safe Email Operations

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.

Dependency Injection for Configuration

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.

Result Validators Plus Oversight

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.

Structured Email Responses

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.

Testable Email Agents

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.


Get started in minutes

Define Dependencies and Tools
python
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.

Register Email Tools
python
@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.

Run the Agent
python
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.

Add Result Validation
python
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.


Step by step

1

Create a MultiMail Account and API Key

Sign up at multimail.dev, create a mailbox, and generate an API key. Your key will start with mm_live_.

2

Install Dependencies

Install Pydantic AI and requests for calling the MultiMail API.

bash
pip install pydantic-ai requests
3

Define Dependencies and Result Types

Create a dataclass for email dependencies (API key, mailbox ID) and a Pydantic model for structured results.

4

Register Tools

Decorate functions with @agent.tool that accept RunContext[EmailDeps] and call MultiMail API endpoints with the injected configuration.

5

Run and Review

Execute the agent with your dependencies. Review pending emails in the MultiMail dashboard when using gated_send mode.

bash
result = await agent.run("Check my inbox", deps=deps)

Common questions

How does dependency injection help with MultiMail integration?
Dependency injection passes your MultiMail API key and mailbox configuration to tool functions through the RunContext, avoiding global state. This makes your agent testable — swap in mock dependencies for testing without changing tool logic — and cleanly separates configuration from behavior.
What's the benefit of typed results for email agents?
Typed results (like EmailResult with summary and actions_taken fields) give your application code structured data instead of raw strings. You can programmatically check what actions the agent took, display summaries in a UI, or feed results into downstream processing — all with type safety and IDE autocomplete.
How do result validators interact with MultiMail's oversight?
Result validators check the agent's output structure and quality, triggering retries if the output is insufficient. MultiMail's oversight checks whether emails should actually be delivered. They operate at different levels: validators ensure the agent reasons well, oversight ensures emails are appropriate to send.
Can I test my email agent without sending real emails?
Yes. Create a mock EmailDeps that returns test responses instead of calling the real API. Pydantic AI's dependency injection makes this straightforward. You can also use MultiMail's staging environment with the gated_send mode to test real API calls without delivering emails.
Does Pydantic AI support async email operations?
Yes. Pydantic AI is async-native with agent.run() being an async function. Use httpx or aiohttp in your tool functions for async HTTP calls to the MultiMail API. The framework handles the async execution lifecycle automatically.

Explore more

The only agent email with a verifiable sender

Email infrastructure built for AI agents. Verifiable identity, graduated oversight, and a 38-tool MCP server. Formally verified in Lean 4.