Your LangChain agent can browse the web, write code, and call APIs.
Can it spend $500 without your permission?
If you have not explicitly set spending limits and an approval
layer, the answer is probably yes.
Curious how others are handling financial guardrails for agents
that touch real money or APIs with billing attached. Is anyone
solving this properly?
@DeboJolaosho, welcome to the LangChain community.
Though I have no direct experience with such a use case, however using HumanInTheLoopMiddleware to pause before payment or billing tools run makes most sense to me (following is just a hypothetical example to showcase the usage of HumanInTheLoopMiddleware in such a case):
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
agent = create_agent(
model="gpt-4o",
tools=\[send_payment, call_paid_api\],
middleware=\[
HumanInTheLoopMiddleware(
interrupt_on={
"send_payment": True, *# always pause*
"call_paid_api": True,
"search_web": False, *# safe tools auto-run*
}
),
\],
checkpointer=InMemorySaver(),
)
config = {"configurable": {"thread_id": "tx_123"}}
result = agent.invoke({"messages": \[...\]}, config=config)
*# User reviews → then resume:*
agent.invoke(Command(resume={"decisions": \[{"type": "approve"}\]}), config=config)
That’s a solid pattern — the interrupt_on approach works well when you know upfront which tools carry financial risk. The limitation I keep running into is that agents in production tend to acquire new tools over time, so the interrupt list drifts. A tool gets added for a different reason and nobody goes back to update the middleware config.
The direction I’ve been exploring is flipping the default: instead of allowlisting specific tools that need approval, enforce a global spending ceiling that fires automatically regardless of which tools are active — more like a circuit breaker than a checkpoint. Curious whether you’ve run into the drift problem in practice, or whether your agents tend to have a stable tool set?
I would go with a stable tool set @DeboJolaosho and enforce a ceiling if needed in the given tool.
Fair point — stable tool sets make the drift problem less severe. The challenge is that “stable” is relative; even disciplined teams add tools incrementally. The ceiling-in-the-tool approach is interesting though — have you implemented that pattern in production, or is it more theoretical at this point?
No, I have not implemented it in Production. However, developing a dedicated MCP if your tools are dynamic and constantly changing might be another recommendation to improve your flow.
If this has helped you and has clarified your confusion, can you please mark the answer as the solution so this thread gets closed down?
Appreciate the MCP suggestion — that’s an interesting angle
for dynamic tool sets.
Before closing: I ended up shipping the enforcement layer as
a standalone package — pip install langchain-valta. Agent calls
a check_spend tool before any paid operation, policy engine
approves or denies before the charge fires. Works today without
MCP. Leaving it here in case it’s useful to anyone who finds
this thread.
IMO, introducing a naming convention for critical tools — such as prefixing them with critical_*— would be a cleaner and more scalable approach. With this in place, you could either adapt the humanInTheLoopMiddleware accordingly, or even propose an enhancement to the official implementation to support wildcard matching (e.g., critical_*) or regex-based filtering
This way, the toolset remains dynamic while the middleware stays robust and maintainable. Even if new tools are added in the future, as long as they follow the agreed prefix convention, they will automatically integrate into the existing control flow without additional changes
The drift problem is exactly what caught our attention too.
We’ve been experimenting with moving checks away from individual tools and closer to the execution boundary itself. That way new tools don’t automatically bypass existing controls.
One thing we found is that schema-valid doesn’t always mean safe-to-execute.
Curious — is your circuit breaker evaluating the action itself, or just tracking spend after execution?
Good distinction. Valta evaluates before execution — the check fires before the tool call goes through, not after. So it’s pre-execution admission control, not post-hoc tracking. Schema-valid but over-budget = blocked. The spend record is written only if approved. Same pattern as your execution-boundary approach — just applied specifically to the cost/budget dimension. valta.co