Skip to main content

x402-sdk

x402 clients for AI agents: read the 402, pay, retry. One wire contract, one library per language, no key ever held here.

x402 is HTTP's own payment step: ask for a resource, get 402 with the terms, pay, ask again with the proof. It is small enough that an SDK's job is not to hide it but to get the four details right that are easy to get wrong.

package install tests
packages/go go get github.com/kotoba-lang/x402-sdk/packages/go go test ./... — 12
packages/typescript@com-kotobalabs/x402 npm i @com-kotobalabs/x402
packages/pythoncom-kotobalabs-x402 pip install com-kotobalabs-x402 once published
packages/rustcom-kotobalabs-x402 cargo add com-kotobalabs-x402 once published cargo test — 11

Published under the com-kotobalabs organisation. x402-sdk, x402 and x402-client were already taken across crates.io, npm and PyPI — checked before publishing rather than after a rejected upload — and the reverse-DNS form says whose it is, which is more useful than a generic name anyway. npm carries it as a scope; PyPI and crates.io have none, so they carry it as a prefix.

Go and Rust install by name today. Go modules are fetched from the repository, so a tag publishes them; npm and PyPI need a token created through a web login, and saying npm i works before anyone has pushed would be a claim about a registry rather than about this code.

The contract both implement is spec/wire.md, transcribed from the facilitator that actually runs rather than from a document it might have drifted from.

The safe first call

from x402_sdk import challenge
c = challenge("https://x402.nexus/gateway/hanmoto/x402/counts")
import { challenge } from "@com-kotobalabs/x402";
const c = await challenge("https://x402.nexus/gateway/hanmoto/x402/counts");

No key, no payment, no side effect. c.accepts states every acceptable payment completely — price, token, network, recipient, and the EIP-712 domain to sign under.

import x402 "github.com/kotoba-lang/x402-sdk/packages/go"
c, _ := (&x402.Client{}).Challenge("https://x402.nexus/gateway/hanmoto/x402/counts")

Paying

const res = await fetchWithPayment(url, undefined, {
  policy: { networks: ["base-sepolia"],
            assets: ["0x036CbD53842c5426634e7929541eC2318f3dCF7e"],
            maxAmount: "5000" },
  signer,                       // yours. See below.
});

You do not need ETH. The exact scheme has the facilitator submit the authorization and pay the gas, so a buyer holding only USDC can pay.

Four things these libraries insist on

The signer is injected (and in Rust, the HTTP transport too). These packages hold no key, sign nothing, and have no cryptography dependency. Your wallet stays wherever you put it — viem, ethers, eth-account, a hardware device, a remote signer. A payment library that wants your key is a place your key can leak from.

A policy is required. selectOffer with no policy refuses with no-policy rather than paying. There is no default ceiling, because a default ceiling is a limit nobody chose.

The cheapest allowed offer wins, never the first. Sellers list the same resource on mainnet and on a testnet in their own order. Taking accepts[0] is how a buyer pays real money to rehearse.

The EIP-712 domain comes from the offer. Base mainnet USDC calls itself USD Coin; Base Sepolia USDC calls itself USDC. Signing under the wrong one produces a signature that recovers a different address, and the payment is rejected as a bad signature — which reads like your fault. These libraries refuse an offer that carries no domain rather than guessing one, and they refuse an unknown network rather than defaulting its chain id.

Refusals are named

Every refusal says which check failed, and all of them, not just the first: an offer on the wrong network usually names that network's token too, and a caller told only network-not-allowed will fix the network and be refused again.

no-policy · scheme-not-allowed · network-not-allowed · asset-not-allowed
over-budget · unparseable-amount · not-a-challenge · no-offers

What they do not do

They do not settle, do not hold funds, and do not implement the transaction scheme's transfer — an offer asking for it is refused by name. They are the protocol, not the wallet.

Measured against a live facilitator

challenge: parsed | offers: 2
  testnet         -> base-sepolia 1000 units domain=USDC/2     chainId=84532
  mainnet-only    -> base         1000 units domain=USD Coin/2 chainId=8453
  1-unit ceiling  REFUSED asset-not-allowed+network-not-allowed+over-budget
catalog: 25 SKUs

MIT.

Release files for com-kotobalabs-x402 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for com-kotobalabs-x402 0.1.0
File Size Uploaded
com_kotobalabs_x402-0.1.0.tar.gz 7.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for com-kotobalabs-x402 0.1.0
File Interpreter ABI Platform
com_kotobalabs_x402-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 15.0 kB

Release files / com_kotobalabs_x402-0.1.0.tar.gz

Download URL com_kotobalabs_x402-0.1.0.tar.gz
Size 7.8 kB
Tags Source
SHA-256 checksum
How to use checksums
0fa0436342e178b0d3fa6e42c0907495e1f23df1501bde40b0d5465b5181cf99
BLAKE2b-256 checksum
How to use checksums
d5cbe88ef216a03331f2bb6b7dbdee5a5fa8a07dddff8fd9683d26f755997321
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.9 {"installer":{"name":"uv","version":"0.9.9"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / com_kotobalabs_x402-0.1.0-py3-none-any.whl

Download URL com_kotobalabs_x402-0.1.0-py3-none-any.whl
Size 7.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
00fc7fa392d8795364b3659e9c17dd544f70380b31f47db634324a2618c41531
BLAKE2b-256 checksum
How to use checksums
e2b3c0b380aba3ea76c008fd59a562f14165e2bdb82089115fc0aa95e079417e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.9 {"installer":{"name":"uv","version":"0.9.9"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release 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