Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nevermined.app/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers the LangChain and LangGraph integration shipped in the [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.

Installation

pip install payments-py[langchain] langgraph langchain-openai
The [langchain] extra installs langchain-core. langgraph and langchain-openai are optional — needed only if you build a LangGraph agent.

Exports

SymbolPurpose
requires_paymentDecorator that gates a @tool on an x402 access token
PaymentRequiredErrorException raised when the token is missing or verification fails
last_settlementRead the most recent settlement receipt from outside the agent’s call stack
create_paid_react_agentLangGraph ReAct agent builder that lets PaymentRequiredError propagate intact
All four are exported from payments_py.x402.langchain:
from payments_py.x402.langchain import (
    requires_payment,
    PaymentRequiredError,
    last_settlement,
    create_paid_react_agent,
)

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

def requires_payment(
    payments: Payments,
    plan_id: str | None = None,
    plan_ids: list[str] | None = None,
    credits: int | Callable[[dict], int] = 1,
    agent_id: str | None = None,
    network: str | None = None,
    scheme: str | None = None,
) -> Callable
ParameterDescription
paymentsA Payments instance with payments.facilitator configured.
plan_id / plan_idsThe plan(s) the tool charges against. Pass one of the two; plan_id is a convenience for the single-plan case.
creditsHow many credits to charge. Sent to the facilitator as max_amount. See credits semantics for fixed-vs-range plans.
agent_idOptional agent identifier propagated to the receipt.
networkOptional CAIP-2 network ID. Auto-resolved from the plan when omitted.
schemeOptional x402 scheme (nvm:erc4337 or nvm:card-delegation). Auto-resolved when omitted.

Decorator order

@tool outside, @requires_payment inside:
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from payments_py import Payments, PaymentOptions
from payments_py.x402.langchain import requires_payment

payments = Payments.get_instance(
    PaymentOptions(nvm_api_key="nvm:builder-key", environment="sandbox")
)

@tool
@requires_payment(payments=payments, plan_id="plan-123", credits=1)
def get_market_insight(topic: str, config: RunnableConfig = None) -> str:
    """Return a short market insight. Costs 1 credit per call."""
    return f"Market insight for '{topic}': demand is up 12% QoQ."
The protected function must accept a config: RunnableConfig parameter — that is how the decorator reads the payment token at call time.

Dynamic credits

credits accepts three forms:
  • Static intcredits=1 (fixed cost per call).
  • Lambdacredits=lambda ctx: max(1, len(ctx["result"]) // 100).
  • Named functioncredits=my_fn where my_fn(ctx) -> int.
When callable, ctx is {"args": <tool kwargs>, "result": <tool return>}. Dynamic credits resolve after execution so the result is available.

Credits semantics

The credits 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 burn plan.credits.maxAmount. The decorator’s credits=N is then effectively a no-op (per nevermined-io/nvm-monorepo#1568).
  • Range plans clamp the supplied value into [plan.credits.minAmount, plan.credits.maxAmount].
If you want predictable per-call cost, configure the plan as fixed; the decorator value is then a client-side declaration.

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.
from payments_py.x402.langchain import PaymentRequiredError

try:
    agent.invoke({"messages": [("human", "...")]}, config={"configurable": {}})
except PaymentRequiredError as err:
    accepts = err.payment_required.accepts[0]
    # accepts.scheme    → "nvm:erc4337" or "nvm:card-delegation"
    # accepts.network   → CAIP-2 chain or provider name (stripe, braintree, visa)
    # accepts.plan_id   → which plan to acquire a token against

Attributes

AttributeTypeDescription
payment_requiredX402PaymentRequired | NoneThe full x402 v2 payment-required payload. accepts[0] carries scheme / network / plan_id / agent_id.
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

def last_settlement() -> SettleResponse | None
Returns None if no settlement has happened yet in this process, or if the most recent invocation raised before reaching the settle phase.

Example

from payments_py.x402.langchain import last_settlement

result = agent.invoke(
    {"messages": [("human", "...")]},
    config={"configurable": {"payment_token": token}},
)

receipt = last_settlement()
if receipt:
    print(f"  credits_redeemed: {receipt.credits_redeemed}")
    print(f"  remaining_balance: {receipt.remaining_balance}")
    print(f"  transaction: {receipt.transaction}")
last_settlement() reads from a module-level slot. In multi-tenant processes (e.g. a server handling concurrent settlements), the value reflects whichever invocation settled most recently — there is no per-call isolation. For multi-tenant scenarios, surface settlement via a callback or observability layer instead.

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

def create_paid_react_agent(
    model: Any,
    tools: Sequence[Any],
    **kwargs: Any,
) -> CompiledStateGraph
ParameterDescription
modelAnything accepted by create_react_agent’s model argument (a BaseChatModel, a string, a runnable, etc.).
toolsSequence of LangChain tools — typically functions decorated with @tool and @requires_payment.
**kwargsForwarded verbatim to create_react_agent (prompt, state_schema, checkpointer, …). Unknown kwargs raise TypeError from LangGraph at call time.
langgraph is imported lazily so the [langchain] extra need not pull it in. Install LangGraph yourself (pip install langgraph) to use this helper.

Example

from langchain_openai import ChatOpenAI
from payments_py.x402.langchain import create_paid_react_agent, requires_payment

@tool
@requires_payment(payments=payments, plan_id=PLAN_ID, credits=1)
def get_market_insight(topic: str, config=None) -> str:
    return f"Market insight for {topic} ..."

agent = create_paid_react_agent(
    ChatOpenAI(model="gpt-4o-mini", temperature=0),
    [get_market_insight],
    prompt="You are a market data assistant. Always call get_market_insight.",
)

End-to-end usage

The canonical x402 flow uses all four symbols together — discovery, acquisition, retry, receipt read:
import os
from dotenv import load_dotenv
from payments_py import Payments, PaymentOptions
from payments_py.x402.langchain import (
    PaymentRequiredError,
    create_paid_react_agent,
    last_settlement,
    requires_payment,
)
from payments_py.x402.types import DelegationConfig, X402TokenOptions

load_dotenv()
payments = Payments.get_instance(
    PaymentOptions(
        nvm_api_key=os.environ["NVM_API_KEY"],
        environment=os.getenv("NVM_ENVIRONMENT", "sandbox"),
    )
)

# 1. Build the agent (see the LangChain integration guide for the protected tool).
agent = create_paid_react_agent(model, [get_market_insight], prompt=prompt)

# 2. Discover what the agent's tool charges by invoking without a token.
try:
    agent.invoke({"messages": [("human", QUERY)]}, config={"configurable": {}})
except PaymentRequiredError as err:
    accept = err.payment_required.accepts[0]

# 3. Acquire a token against the discovered plan.
pm = next(m for m in payments.delegation.list_payment_methods()
          if m.provider == accept.network)
token = payments.x402.get_x402_access_token(
    accept.plan_id,
    token_options=X402TokenOptions(
        scheme=accept.scheme,
        delegation_config=DelegationConfig(
            provider_payment_method_id=pm.id,
            spending_limit_cents=10000,
            duration_secs=3600,
            currency="usd",
        ),
    ),
)["accessToken"]

# 4. Retry with the token and read the settlement.
result = agent.invoke(
    {"messages": [("human", QUERY)]},
    config={"configurable": {"payment_token": token}},
)
receipt = last_settlement()
For the full, runnable version see tutorials/langchain-paid-agent-py.