Skip to main content

langchain-x402-sagg

A LangChain Tool that pays for SAGG chat-completion requests per-request, via the x402 protocol (a signed crypto micropayment) instead of an API key. Built for autonomous agents that need to call an LLM but have no human available to hold an API key or enter a credit card - only a funded wallet.

Status: extensively tested on Base Sepolia testnet; one real, successful payment completed on Base mainnet. Not published to PyPI or GitHub - prepared for review, not distributed. See "Why this exists" below for the real gap this fills, and Security below before pointing this at mainnet yourself - a single successful payment is real evidence the mechanics work, not a claim of production-grade battle-testing.

Why this exists

LangChain has no built-in way to pay for a tool call via x402 - see langchain-ai/langchain#36306 (open, unresolved). Every SAGG endpoint already speaks x402 (see the main sagg-project repo's own x402module/ and cmd/gateway/x402billing.go) - this package is the missing piece that lets a LangChain agent actually use it, without a human provisioning an API key first.

Why pure Python, not a wrapper around the existing Go client

SAGG's own x402 client/server code (x402module/poc/, part of the main sagg-project repo) is written in Go. Two ways to give LangChain (Python-only) access to it were considered:

  1. Wrap the Go module via a subprocess or a local HTTP bridge.
  2. Reimplement the signing logic in Python (this package's choice).

EIP-712 typed-data signing and EIP-3009 (transferWithAuthorization) are open cryptographic standards, not anything Go-specific - Python has a mature, widely-used implementation already (eth_account, part of the same ecosystem web3.py is built on). A subprocess/HTTP bridge would mean shipping (or requiring the user to build) a platform-specific Go binary alongside a pip install-able package - real packaging and distribution friction for what should be a lightweight tool, and exactly the kind of "invented, fragile workaround" this project's own standing rule warns against when a cleaner path exists. Reimplementing ~150 lines of well-specified, independently testable signing logic in idiomatic Python was the more honest engineering call, not the shortcut - and it's independently verified: tests/test_signer.py proves a produced signature actually recovers to the signer's own address (not just "no exception was raised"), and tests/test_live_sagg_x402.py proves the whole flow against a real, running SAGG endpoint on real Base Sepolia testnet infrastructure.

Installation

pip install -e .

(Not published to PyPI - install from a local checkout.)

Usage

from langchain_x402_sagg import make_sagg_x402_tool

tool = make_sagg_x402_tool(
    private_key="0x...",              # a Base Sepolia testnet EVM private key, funded with testnet USDC
    sagg_url="https://your-sagg-gateway.example.com",
)

result = tool.invoke({"prompt": "What is the capital of France?"})
print(result)

Use it in an agent like any other LangChain tool:

from langchain.agents import create_agent  # or your framework's own agent constructor

agent = create_agent(model=your_llm, tools=[tool])

Configuration

make_sagg_x402_tool accepts:

Argument Default Meaning
private_key required Hex EVM private key. Testnet only - see Security below.
sagg_url required Base URL of the SAGG gateway (no trailing /v1/...).
model "deepseek-ai/DeepSeek-V4-Flash-0731" Model id to request.
max_amount_atomic 1_000_000 (1.00 USDC) Hard per-call payment ceiling, in USDC's smallest unit (6 decimals). A request whose price exceeds this is refused before any signature is produced - never silently paid over the limit.
network "base-sepolia" The network this client signs for. Must match the network the target sagg_url actually serves - see "A real safety check" below. "base" (mainnet) works and has been used for one real payment, but is far less tested than testnet - see Security.

How it works

  1. POST the prompt to {sagg_url}/v1/chat/completions.
  2. If the response is 402 Payment Required, parse the price (accepts[0]) from the challenge body.
  3. Refuse if the price exceeds max_amount_atomic.
  4. Sign a fresh EIP-3009 TransferWithAuthorization for the exact amount, to the exact recipient, with a fresh random nonce (a nonce is never reused - see signer.py's own doc comment on why that's the actual replay-protection mechanism, not anything this client itself does).
  5. Repost the SAME prompt with an X-PAYMENT header carrying the signed payload.
  6. Return the model's response text as the tool's output.

A real safety check, added after a real failure

The first live mainnet attempt failed - not because the payment logic was broken, but because the client signed with network's configured chain id (defaulting to Base Sepolia's 84532) while the server it was actually pointed at was issuing a real mainnet (8453) challenge. The signature was well-formed but cryptographically wrong for the chain the server expected, and the facilitator correctly rejected it. Root cause: nothing checked that the network this client was told to sign for actually matched the network the server's own 402 challenge named.

Fixed: before signing, post_with_payment now compares the challenge's own network field against this client's configured network and raises SaggX402PaymentError immediately on any mismatch - before any signature is ever produced. If you see this error, it means network= doesn't match the sagg_url you're actually calling; fix the argument, don't work around the check.

Testing

pip install -e ".[dev]"
pytest tests/test_signer.py tests/test_client.py -v   # fast, no network - signing + network-validation logic

The live end-to-end test moves real (testnet) funds and needs a reachable SAGG endpoint, so it's opt-in:

SAGG_X402_TEST_PRIVATE_KEY=0x... \
SAGG_X402_TEST_URL=http://localhost:18080 \
RUN_LIVE_X402_TEST=1 \
pytest tests/test_live_sagg_x402.py -v -s

This was run for real against a locally-running SAGG gateway instance (Base Sepolia, real facilitator, real on-chain settlement) during this package's own development - a real partial-refund cycle (ceiling 0.000326 USDC, actual 0.000002 USDC, refund 0.000324 USDC), with both the ceiling and refund transactions independently confirmed on-chain (eth_getTransactionReceipt, status: 0x1) rather than trusted from the gateway's own response alone. This same package was later also used, with network="base", for the first real payment on Base mainnet - independently confirmed on-chain the same way.

Security

  • Testnet is where this package has real depth of testing; mainnet has one real, successful payment. Base Sepolia testnet has been exercised extensively during development. Base mainnet (network="base") works - it's the same code path, the same signing logic, and one real payment has gone through it successfully with independent on-chain confirmation - but "it worked once" is real evidence, not a claim of thorough production battle-testing. Treat a mainnet-funded private_key with the caution that implies: use a wallet holding only what you're willing to lose, and keep max_amount_atomic tight.
  • The private key is held in plain Python memory for the process's lifetime - the same custody model SAGG's own Go server wallet uses today (see the main sagg-project repo's Stage 3 security audit, PROJECT_STATE.md sec 3.147, for a full discussion of what that means and why it's a real, disclosed limitation, not a production-ready custody solution). Use a wallet that holds only what you're willing to lose.
  • max_amount_atomic is a real, enforced ceiling (checked before any signature is produced), but it is a courtesy limit on THIS client's own behavior - it does not protect against a malicious or compromised server issuing a valid-looking but overpriced challenge below that ceiling. Set it to the smallest value your actual use case needs.

What this package does NOT do

  • Publish itself anywhere (no PyPI, no GitHub) - prepared for review only, per its own scope.
  • Support streaming responses (SAGG's own streaming mode exists; this tool's first version only handles the non-streaming JSON response shape).
  • Implement any provider failover, retries, or the fuller reliability behavior SAGG's own Stripe-billed path has - this is a minimal, first-version client.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langchain_x402_sagg-0.1.0.tar.gz (18.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langchain_x402_sagg-0.1.0-py3-none-any.whl (13.1 kB view details)

Uploaded Python 3

File details

Details for the file langchain_x402_sagg-0.1.0.tar.gz.

File metadata

  • Download URL: langchain_x402_sagg-0.1.0.tar.gz
  • Upload date:
  • Size: 18.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.4

File hashes

Hashes for langchain_x402_sagg-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9f2870a45be9c821c54ded04ff86ee166e8d8b9d5ec03394e5e23f01f3589843
MD5 39622f3b3b6caf8dcda92894a5bc4920
BLAKE2b-256 f6d1f50c2435ec59e4fe061ab4c7d3b69530f9ab282e824dfce145f7e2539e3e

See more details on using hashes here.

File details

Details for the file langchain_x402_sagg-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for langchain_x402_sagg-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c17266d5161740c90d72fea3511457c402b4b2d9d102ba4f1fb94ea8e9f92b2f
MD5 ba544e0f655561b26c4234fe284c8b0e
BLAKE2b-256 7ae21f189ba6105f569a582b4624205097a84760439ed5daed8561b92ab74444

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page