MailFlatDocs
Documentation/AI agents & assistants/LangChain

Using MailFlat with LangChain

MailFlatToolkit turns the Python SDK into LangChain tools, so an agent can open inboxes and read verification codes as part of its normal tool loop.

How it fits together

The toolkit is a thin shell over the same client you would call directly: every tool wraps one SDK method and returns a plain dict, and errors come back as {"error": "..."} instead of raising. That matters for an agent — a raised exception ends the run, a returned error is something the model can read and work around.

The tools

Six here, not seven: the toolkit has no delete_message, so a LangChain agent can drop a whole inbox but not quietly remove one message from it.
ToolArgumentsWhat it does
create_inboxprefix? · label? · retention_hours?Opens a real inbox and returns its address.
list_inboxesEvery inbox this API key can see.
read_messagesaddressAll messages in an inbox, newest first.
wait_for_otpaddress · timeout?Polls until a one-time code arrives, then returns it.
send_emailaddress · to · subject? · body? · html?Sends a DKIM-signed email from the inbox.
delete_inboxaddressDeletes the inbox and every message in it.

Install

langchain-core is an optional extra, so the plain package stays dependency-free for people who only want the client.
Shell
pip install "mailflat[langchain]"

Build the tools

get_tools() returns the six tools as LangChain BaseTool objects — ready for any agent that accepts a tool list.
Python
import os
from mailflat.langchain import MailFlatToolkit
 
toolkit = MailFlatToolkit(api_key=os.environ["MAILFLAT_API_KEY"])
tools = toolkit.get_tools()
 
print([t.name for t in tools])
# ['create_inbox', 'list_inboxes', 'read_messages',
# 'wait_for_otp', 'send_email', 'delete_inbox']

Hand them to an agent

Nothing about MailFlat is special here: the tools go in the same list as the rest, and the model decides when an inbox is needed.
Python
from langchain.chat_models import init_chat_model
from langgraph.prebuilt import create_react_agent
 
llm = init_chat_model("claude-opus-5", model_provider="anthropic")
agent = create_react_agent(llm, tools)
 
result = agent.invoke({
"messages": [(
"user",
"Open an inbox labelled langchain-demo, tell me its address, "
"then wait up to 60 seconds for a one-time code and report it.",
)],
})
print(result["messages"][-1].content)

Give it fewer tools than you have

An agent that only reads mail has no reason to hold delete_inbox. Filtering the list is the whole mechanism — there is no separate permission model.
Python
READ_ONLY = {"create_inbox", "read_messages", "wait_for_otp"}
tools = [t for t in toolkit.get_tools() if t.name in READ_ONLY]
Pair this with a short retention_hours so inboxes still disappear even though the agent cannot delete them.

Share one client between the toolkit and your own code

Pass a client in and the toolkit stops building its own. Useful when your code and the agent must talk to the same base URL, or when a test injects a fake.
Python
from mailflat import MailFlat
from mailflat.langchain import MailFlatToolkit
 
mf = MailFlat(base_url="https://mail.example.com")
toolkit = MailFlatToolkit(client=mf)
 
inbox = mf.create(label="shared", retention_hours=2) # your code
agent_tools = toolkit.get_tools() # the agent's

Worth knowing

Tools return errors as data, not exceptions — so a model that asks for a code that never arrives sees {"error": "..."} and can decide to wait again. Log those returns; a silent retry loop is expensive.
wait_for_otp blocks the whole agent while it polls. Keep the timeout to what the mail actually needs (30-60 seconds) instead of the largest number that feels safe.
An encrypted inbox returns an error rather than a code, by design: the server cannot decrypt it. Create agent inboxes without encryption.