API v1Private beta. The API is stable; send limits are low while the shared domain warms up.

Integrations

LangChain for Python

langchain-send0: a send0 toolkit for LangChain and LangGraph agents.

langchain-send0 gives LangChain agents send0's tools as BaseTool classes with pydantic argument schemas. Each tool runs sync (invoke) and async (ainvoke, on send0's async client). It needs Python 3.10+ and langchain-core 1.x.

Install

pip install langchain-send0

Example

from langchain.agents import create_agent
from langchain_send0 import Send0Toolkit

tools = Send0Toolkit(exclude=["send_email", "reply"]).get_tools()  # reads SEND0_API_KEY
agent = create_agent("anthropic:claude-opus-5-5", tools=tools)

result = agent.invoke({"messages": [{"role": "user", "content": "Summarize the latest conversations in my inbox."}]})

A runnable version, with approval for every send, is in examples/langchain-python-agent.

Options

Send0Toolkit(client=None, api_key=None, inbox_id=None, include=None, exclude=None, *, async_client=None, base_url=None)

Option
clientA configured send0.Send0. By default the toolkit makes one from api_key
api_keyDefaults to SEND0_API_KEY
inbox_idDefault inbox, so the model doesn't need inbox_id
includeOnly these tools
excludeAll tools but these
async_clientA configured send0.AsyncSend0 for ainvoke. Without one, async calls use a thread
base_urlYour self-hosted send0

Each tool is also a class you can use alone, such as WaitForEmailTool(client=Send0(), default_inbox_id="ibx_…").

Approval

send_email and reply have sends = True. To have a person approve them before they run, add LangChain's human-in-the-loop middleware. It pauses the agent with an interrupt, so it needs a checkpointer:

from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver

agent = create_agent(
    "anthropic:claude-opus-5-5",
    tools=Send0Toolkit().get_tools(),
    checkpointer=InMemorySaver(),
    middleware=[HumanInTheLoopMiddleware(interrupt_on={"send_email": True, "reply": True})],
)

Resume with agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}), config), one decision per pending call.

Untrusted content

Every body the tools return sits inside <untrusted_email> tags with a note to treat it as data. Keep the tool set to what the task needs, use a key limited to one inbox, and put sending behind approval when the agent reads mail from strangers. See Prompt injection.

On this page