LangGraph & LangChain
You have a LangGraph agent, or LangChain tools, and some of those tools should not run without a person saying yes: sending an email, issuing a refund, writing to production. This page shows how to put a human approval step — and a policy check — in front of those tool calls with the Guardyx LangGraph adapter, without restructuring your graph.
Guardyx ships a native LangGraph adapter, GuardyxLangGraphAdapter, and LangChain
tools are governed through that same adapter. Your tools, prompts, and graph stay as they
are; you wrap the tools, and every call is checked before it runs.
What you get
Section titled “What you get”For every wrapped tool call, Guardyx returns one of three decisions:
| Decision | What happens in your agent |
|---|---|
allow |
The tool executes and returns its result. |
deny |
GuardyxDeniedError is raised; the tool body never runs. |
require_approval |
The call blocks while a human reviews it in the portal. On approval the tool runs; on denial, expiry, or timeout the SDK raises. |
Which tools are allowed, denied, or held for review is decided by policies you set in the portal, not by code in your graph. Each governed call is recorded in a tamper-evident run trace with its inputs, the decision, and the outcome.
Install
Section titled “Install”pip install guardyx[langgraph]The langgraph extra covers both LangGraph and LangChain tools. The SDK requires
Python 3.13+ and is async-first.
Register your tools
Section titled “Register your tools”Tool identities live in the portal, not in your code. Create an agent with the
langgraph framework type (use langchain if you’re wrapping LangChain tools — it maps to
the same adapter), register each tool once under Agents → Tools, and copy its UUID. The
Agent created modal shows the agent’s API key. The base URL is https://app.guardyx.ai/v1, or your own host if you self-host.
Wrap your tools
Section titled “Wrap your tools”Construct the adapter, wrap your existing tools with their portal UUIDs, and open one run per agent invocation:
import os
from guardyx import GuardyxLangGraphAdapterfrom langchain_core.messages import HumanMessagefrom langchain_openai import ChatOpenAIfrom langgraph.prebuilt import create_react_agent
adapter = GuardyxLangGraphAdapter( api_key=os.environ["GUARDYX_API_KEY"], agent_id=os.environ["GUARDYX_AGENT_ID"], base_url=os.environ["GUARDYX_BASE_URL"], # https://app.guardyx.ai/v1)
# Your existing LangGraph / LangChain tools, each paired with its portal tool UUID.governed_tools = adapter.wrap_many([ (web_search, "<tool-uuid-1>"), (send_email, "<tool-uuid-2>"), (query_database, "<tool-uuid-3>"),])
async def run_agent(query: str): async with adapter.run(): # one run per invocation agent = create_react_agent(ChatOpenAI(model="gpt-4o"), governed_tools) return await agent.ainvoke({"messages": [HumanMessage(content=query)]})To govern a single tool, use adapter.wrap(tool, "<tool-uuid>"). Wrapping is drop-in:
the wrapped tools go wherever the originals went.
How the approval step works
Section titled “How the approval step works”When policy holds a call for review, the wrapped tool does not return yet. The call shows up in the portal with its full payload and decision context, and the SDK polls until a reviewer approves or denies it. To your graph, an approved call just looks like a slower tool call — there is no interrupt to raise and no resume to wire up.
On approval, the tool runs and its result flows back into the graph. On denial, expiry,
or timeout, the SDK raises a typed error under GuardyxError, so you can decide how the
agent should respond. See Handling decisions and errors.
Compared with LangGraph’s built-in interrupt()
Section titled “Compared with LangGraph’s built-in interrupt()”LangGraph’s own interrupt() and LangChain’s human-in-the-loop middleware pause the graph
and hand control back to your application, which collects the decision and resumes the
run. That is the right tool when the person approving is the person using your app, in
your UI.
Guardyx covers a different case:
- The rule lives outside the graph. Which tools need approval is portal policy, so changing it is not a code change or a redeploy.
- The approver doesn’t have to be the user. Held calls go to the reviewers you set up in Guardyx, not necessarily to whoever is chatting with the agent.
- Every call is on the record. Allowed, held, and denied calls all land in the same tamper-evident run trace, with who approved what and when.
- Nothing to resume. The wrapped tool waits for the decision itself, so you don’t manage checkpoints or resume commands for the approval step.
The two can coexist: keep interrupt() for questions your own user should answer, and
let Guardyx gate the actions that need policy and a reviewer.
Next steps
Section titled “Next steps”- SDK reference — the client, the other framework adapters, error types, and the wire protocol.
- Quickstart (MCP client) — govern Claude Desktop, Claude Code, or Cursor with a config change instead of code.
Every AI action passes through Guardyx.