LangChain gives an agent a loop and a set of tools; it doesn't give that agent anywhere to receive a reply. If you want a LangChain agent that can send a follow-up and later notice someone wrote back, you have to give it an actual mailbox, and then give the agent a way to call it. DimeBox has no official LangChain package for this — there isn't one, and anyone implying otherwise is describing something that doesn't exist. What you're building is a plain HTTP tool, wrapped in LangChain's tool interface, that happens to call a paid API underneath.
Two capabilities cover almost every case: a way to send mail out, and a way to check what's come back. The first costs money on every call — DimeBox never lets outbound email be free — the second doesn't, because reading a mailbox with its own token isn't a metered action. That asymmetry matters for how you design the tools: check_inbox is safe for an agent to call on every reasoning step; send_inbox_email is not, and should only fire once the agent actually has something worth sending.
Don't make inbox creation a tool the agent calls — it's a one-time setup step you run yourself, the same way you'd provision a database before pointing an ORM at it. A single unauthenticated POST gets you a random slug and, critically, a read token shown exactly once. Save both before writing a single line of agent code — there's no recovery flow if you lose the token.
curl -X POST https://dimebox.app/api/inboxes
# → { "data": { "slug": "bold-ember-564", ... }, "read_token": "inbox_..." }
The send endpoint is paid, so the tool function needs to carry the full x402 pay-and-retry loop, not just a bare HTTP call. The Python quickstart covers this loop in isolation; here it's dropped straight into a LangChain tool definition using the current @tool decorator from langchain_core:
import base64, json, os
import httpx
from langchain_core.tools import tool
from x402 import x402Client
from x402.mechanisms.evm.exact import ExactEvmScheme
signer = ... # your EVM signer, loaded from an env var
client = x402Client()
client.register("eip155:84532", ExactEvmScheme(signer=signer)) # Base Sepolia
INBOX_SLUG = os.environ["DIMEBOX_SLUG"]
READ_TOKEN = os.environ["DIMEBOX_READ_TOKEN"]
async def pay_and_post(url: str, body: dict) -> dict:
async with httpx.AsyncClient() as http:
resp = await http.post(url, json=body)
if resp.status_code == 402:
payment_required = json.loads(base64.b64decode(resp.headers["PAYMENT-REQUIRED"]))
payload = await client.create_payment_payload(payment_required)
signature = base64.b64encode(json.dumps(payload).encode()).decode()
resp = await http.post(url, json=body, headers={"PAYMENT-SIGNATURE": signature})
resp.raise_for_status()
return resp.json()
@tool
async def send_inbox_email(to: str, purpose: str) -> str:
"""Send an email from this agent's inbox. Each call costs real money - use it
only once you actually have something worth sending."""
url = f"https://dimebox.app/api/inboxes/{INBOX_SLUG}/send"
result = await pay_and_post(url, {"to": to, "purpose": purpose})
return f"queued: {result}"
Reading is free and synchronous, so this tool is simpler — no payment client involved at all, just the read token:
@tool
def check_inbox() -> str:
"""Read this agent's inbox for new messages. Free - safe to call often."""
resp = httpx.get(
f"https://dimebox.app/api/inboxes/{INBOX_SLUG}/messages",
headers={"X-Inbox-Token": READ_TOKEN},
)
resp.raise_for_status()
return json.dumps(resp.json())
Bind both to whatever agent runner you're using — a LangGraph create_react_agent, or a hand-rolled loop that calls llm.bind_tools([send_inbox_email, check_inbox]). From the model's point of view these are just two more functions it can call; the model has no idea one of them involves signing a cryptographic payment underneath.
Neither INBOX_SLUG nor READ_TOKEN needs to appear anywhere the model can see them — not in a system prompt, not in a tool's returned string, not in a few-shot example. Both are closed over by the tool functions themselves, loaded once from the environment at process start. A model that never sees the token can't leak it into a transcript, a log line it decides to "summarize," or a reply it drafts to the wrong recipient. The same applies to the EVM signer: it lives inside pay_and_post, never passed in as an argument a tool call could expose.
If send_inbox_email keeps raising after the retry — not a clean 402 handled by pay_and_post, but an exception surviving past it — that's a signal worth surfacing to the agent as a distinct kind of failure, not folded into the same bucket as "the recipient address was invalid." A 422 means the request itself was malformed and the model should fix its arguments; a payment failure surviving the retry usually means something's wrong with the signer, the network, or the facilitator, and no amount of re-prompting the model will fix it. Returning a different error shape for each lets whatever's driving the agent decide whether to retry immediately, back off, or stop and page a human.
Because every call to send_inbox_email has a real price attached, the tool's docstring is doing more work than a typical LangChain tool description — it's the only thing standing between the model and an accidental spend. Be explicit about when the tool should and shouldn't be called, with the same care you'd put into a prompt that controls what an AI-drafted email actually says. A vague description ("sends email") invites a model to call it speculatively; a description that states the cost and the precondition invites it to call it deliberately.
Full endpoint and payment details live in the API reference; the quickstart there is the fastest way to confirm your signer and network are configured correctly before wiring any of this into an agent loop.