[langchain] optional extra of payments-py. The module payments_py.x402.langchain provides the decorator, helpers, and exceptions for monetizing LangChain tools with the x402 protocol.
For the conceptual walk-through (two integration approaches, the discovery-first flow, dynamic credits patterns), see the LangChain integration guide. For a runnable end-to-end demo, see the langchain-paid-agent-py tutorial.
Looking to gate a LangSmith Deployment entry point (/threads/{id}/runs/wait etc.) rather than individual tools? See LangSmith Deployment Middleware. The two integrations are complementary: the decorator covered here protects tools the agent calls; the LangSmith Deployment middleware protects the agent’s HTTP entry point.
Installation
[langchain] extra installs langchain-core. langgraph and langchain-openai are optional — needed only if you build a LangGraph agent.
Exports
All four are exported from
payments_py.x402.langchain:
requires_payment
Decorator that protects a LangChain @tool with x402 payment verification and settlement. Pulls the access token from RunnableConfig.configurable["payment_token"], verifies it, runs the tool body, then settles credits.
Signature
Decorator order
@tool outside, @requires_payment inside:
config: RunnableConfig parameter — that is how the decorator reads the payment token at call time.
Dynamic credits
credits accepts three forms:
- Static int —
credits=1(fixed cost per call). - Lambda —
credits=lambda ctx: max(1, len(ctx["result"]) // 100). - Named function —
credits=my_fnwheremy_fn(ctx) -> int.
ctx is {"args": <tool kwargs>, "result": <tool return>}. Dynamic credits resolve after execution so the result is available.
Credits semantics
Thecredits argument is sent to the facilitator as max_amount. The actual amount redeemed depends on the plan’s server-side credit configuration:
- Fixed plans (
plan.credits.minAmount == plan.credits.maxAmount) always burnplan.credits.maxAmount. The decorator’scredits=Nis then effectively a no-op (per nevermined-io/nvm-monorepo#1568). - Range plans clamp the supplied value into
[plan.credits.minAmount, plan.credits.maxAmount].
PaymentRequiredError
Raised by @requires_payment when the token is missing from config["configurable"]["payment_token"], or when the facilitator rejects the token (expired, invalid signature, insufficient balance, etc.). Carries the full X402PaymentRequired payload so callers can run the x402 discovery flow.
Attributes
For the discovery → acquire → retry flow, see the LangChain integration guide.
last_settlement
Returns the most recent SettleResponse produced by @requires_payment in this process. Use this after invoking a LangGraph runnable to recover the settlement receipt — credits_redeemed, remaining_balance, transaction, network, payer — without threading it back through RunnableConfig.configurable (which LangGraph copies per node, so the SDK’s in-place write is not visible to the outer caller).
Signature
None if no settlement has happened yet in this process, or if the most recent invocation raised before reaching the settle phase.
Example
create_paid_react_agent
Thin wrapper over langgraph.prebuilt.create_react_agent that constructs the underlying ToolNode with handle_tool_errors=False. That single change is what lets PaymentRequiredError propagate all the way back to agent.invoke()’s caller with its X402PaymentRequired payload intact — the default ToolNode behaviour stringifies the exception into a ToolMessage for the LLM and loses the payload.
Signature
langgraph is imported lazily so the [langchain] extra need not pull it in. Install LangGraph yourself (pip install langgraph) to use this helper.
Example
End-to-end usage
The canonical x402 flow uses all four symbols together — discovery, acquisition, retry, receipt read:tutorials/langchain-paid-agent-py.
Observability with LangSmith
When the optional[langsmith] extra is installed and a LangSmith run is active in the calling context, @requires_payment automatically emits two child spans nested under the active tool span:
nvm:verify— opens around the verify-permissions call, with attributes describing the scheme, plan, payer, and verify duration.nvm:settlement— opens around the settle-permissions call, with attributes describing credits redeemed, remaining balance, transaction hash, network, and settle duration.
nvm.* metadata is also attached to the parent tool span so the trace is searchable from either level. The per-call child spans are always authoritative; the parent copy is best-effort and can be overwritten when two protected tools run in the same node — see Known limitations.
Install
Enable
@requires_payment decorator — the spans are emitted automatically when LangSmith is active. If langsmith is not installed or LANGSMITH_TRACING is unset, span emission is a silent no-op.
Regional endpoint
LangSmith hosts accounts across several regions. The SDK defaults to GCP US (https://api.smith.langchain.com); accounts in any other region must set LANGSMITH_ENDPOINT or the trace POST will fail with 403 Forbidden on /runs/multipart.
langsmith.utils.LangSmithError warnings.
Span attributes
Sensitive data in traces
Thepayment_token that the buyer passes via config["configurable"]["payment_token"] is captured by LangChain into the parent tool span’s metadata, and would normally be inherited by any child span — including the nvm:verify and nvm:settlement spans the decorator emits. The full token grants access to the protected tool until it expires, so the decorator proactively strips payment_token from the parent tool span’s metadata before opening any child span. The full credential never reaches a Nevermined span attribute.
For correlation across spans the decorator surfaces an abbreviated nvm.payment_token attribute (eyJ4NDAyVmVyc2lv…bsig, first 16 chars + ellipsis + last 4) on both nvm:verify and nvm:settlement. That gives you “which token was this?” without exposing the credential itself.
A real x402 access token is a JWT, which is far longer than 20 chars. If a token of 20 characters or fewer is passed — almost always a misconfiguration (a plan id or opaque handle where the JWT was expected) — it is redacted, not exported: nvm.payment_token shows at most the first 4 chars plus a …(short) marker (e.g. eyJ4…(short)), and a runtime warning is logged. For a token of 4 chars or fewer, nothing is revealed at all — it collapses to just …(short). The full short value never reaches a span attribute, so a misrouted secret cannot leak into a durable trace store even when it is shorter than the abbreviation threshold.
The active redaction covers the documented LangChain-via-configurable path. If you’re surfacing the token through a different channel (custom callbacks, an explicit add_metadata({"payment_token": ...}), raw inputs to a tool whose signature contains the token), the decorator can’t see those — strip them yourself or set export LANGSMITH_HIDE_INPUTS=true for blanket coverage.
Other nvm.* attributes that may be considered sensitive depending on your context:
nvm.payer— the payer’s wallet address (public on-chain, but a stable identifier).nvm.tx_hash— the settlement transaction id.nvm.agent_request_id— Nevermined-internal correlation id.nvm.balance.after— the payer’s remaining credit balance after this settlement. Reveals per-payer depletion patterns to anyone with trace read access on the operator’s LangSmith project. Suppress withLANGSMITH_HIDE_OUTPUTS=trueor post-filter.
Known limitations
Parent metadata is last-writer-wins across tools in one node
@requires_payment attaches its nvm.* metadata to two places: the per-call child spans (nvm:verify / nvm:settlement) and, as a convenience for searchability, the parent LangSmith run tree.
The child spans are isolated per call, so they are always correct. The parent copy is not namespaced per tool: the bare nvm.* keys (nvm.tx_hash, nvm.credits_redeemed, nvm.payment_token, …) are written directly onto the parent run’s metadata. When an agent calls two @requires_payment tools within the same LangGraph ToolNode — the common pattern, since a single ReAct step can dispatch multiple tool calls into one node — both decorators target the same parent run tree, and the second add_metadata silently overwrites the first’s nvm.* values. The parent therefore reflects only the last tool that settled in that node; the earlier tool’s parent-level nvm.* is lost.
What this means in practice:
- Per-call billing fidelity lives on the child spans, not the parent. For accurate per-tool accounting (which token, which tx hash, how many credits each call redeemed), filter and aggregate on the
nvm:verify/nvm:settlementchild spans. Each child carries the values for exactly one call. - Treat parent
nvm.*as best-effort. It is convenient for “did this trace touch Nevermined at all?” searches, but do not rely on it for last-writer-sensitive fields when multiple paid tools can run in one node. - This is a last-writer-wins behaviour, not a correctness bug in settlement — every call still verifies and settles independently and correctly. Only the parent’s denormalized copy of the metadata is affected.
nvm-monorepo, which both payments-py and @nevermined-io/payments emit against): child spans are authoritative per tool; parent nvm.* is best-effort / last-writer-wins.
Manual use (non-LangChain paths)
The same context managers are also exported for code that wants to emit Nevermined-flavored spans without going through@requires_payment (e.g. the FastAPI middleware path):
Related
- LangChain integration guide — conceptual walk-through, the two integration approaches (decorator vs. HTTP middleware), and the TypeScript variant.
- x402 Protocol — token generation, delegation config, scheme resolution.
tutorials/langchain-paid-agent-py— the minimal end-to-end demo.