agents-u-cash (Python)
Zero-dependency Python client for the agents.u.cash API - the 402 Online Protocol for non-custodial agent payments. Works for both sides of the network: an agent selling (manage resources, watch settlements) and a buyer (fetch a 402 door, pay, and settle - automatically via on-chain detection, or instantly by submitting the tx hash). Supports ~40 native coins (Bitcoin + Lightning, Ethereum + 15 EVM L2s, Solana, Tron, XRP, USDT/USDC, USDC-on-Base gasless, UCASH) plus custom tokens on 20 chains (ERC-20 / TRC-20 / SPL / ...) - see agents.u.cash for the full live list.
Uses only the standard library (urllib). No dependencies. Python 3.7+.
Highlights
- Monetize AI agents pay-per-call: sell priced resources over HTTP 402; buyers pay direct to your own wallet.
- Non-custodial: the platform never holds funds. 40+ chains (BTC + Lightning, ETH + 15 EVM L2s, SOL, TRX, XRP, USDC-on-Base, UCASH, custom tokens).
- Zero-dependency Python client (3.7+, stdlib
urllib) + a JavaScript SDK + an MCP server (77 tools). - Scoped subagent (RBAC) keys, per-caller spend caps, HMAC-signed settlement webhooks with a built-in verifier.
- Two-sided: sell (resources, one-off payment links, shop, stores, landers, payout-info/OTC) and buy (402 door, UCP checkout sessions).
Docs: agents.u.cash · OpenAPI · llms.txt · CHANGELOG
Contents
- Install · Sell · Buy · UCP checkout sessions
- Manage your store · Subagents · Spend caps
- Verifying webhooks · Asset codes · Resource vs payment request
- API · Error handling
Install
pip install agents-u-cash
Or copy agents_u_cash.py - it has no dependencies.
Sell (as an agent)
from agents_u_cash import AgentsUCash
# Get an API key once (wallet-first; works at $0 immediately - verify email optionally for free credit):
# key = AgentsUCash().signup(email="me@agent.dev", password="longpass")["api_key"]
agent = AgentsUCash(api_key="...")
agent.set_wallet("btc", "bc1q…")
agent.set_webhook("https://my.bot/webhook")
res = agent.create_resource(amount=0.05) # priced resource; res["checkout_url"] = pay.u.cash door
acc = agent.create_challenge(res["res_id"]) # what a buyer pays
# Share res["checkout_url"] - the pay.u.cash buyer door (/checkout/<enc>?cloud=<token>):
# a human opens the HTML checkout, an agent fetches it + "?agent=1" for the 402 manifest,
# an x402 client sends X-PAYMENT. (https://agents.u.cash/r/{res_id} is a back-compat alias.)
# Exact vs dust: save 3+ addresses per coin (agent.set_wallet("ucash", "0xaaa...,0xbbb...,0xccc..."))
# for exact-amount unique-address payments (500.00000000); one address adds sub-unit dust (500.00004180).
# One-off link instead of a persistent resource (closes when paid or after expiry):
req = agent.create_payment_request(amount=5, expiry="24h") # -> {"url": "https://pay.u.cash/id/<enc>"}
settled = agent.get_settlements() # your earnings log
Buy (as a buyer)
from agents_u_cash import AgentsUCash
buyer = AgentsUCash() # no key needed to buy
door = buyer.view_door(res_id) # the 402 door (JSON)
# 1. pick an entry, pay entry["payTo"] exactly entry["amount"] from your wallet (out of band)
# 2. the platform auto-detects the on-chain payment and settles it.
# Optionally POST the tx hash to settle instantly instead:
result = buyer.verify(door["accepts"][0]["challengeId"], tx_hash)
# -> {"settled": True} | {"status": "pending", "confirmations": n, "required": m}
A human-friendly payable page is also available: buyer.view_door(res_id, html=True) returns the HTML.
UCP checkout sessions (buyer)
Multi-item, mixed-currency carts over the Universal Commerce Protocol. The merchant is resolved from the custom-domain base_url, or from a cloud merchant token on the shared host. No API key.
buyer = AgentsUCash() # base_url = the merchant's domain (or the platform host + cloud)
cart = buyer.create_checkout(
line_items=[{"item": {"id": res_id_a}, "quantity": 1}, {"item": {"id": res_id_b}, "quantity": 2}],
currency="USD", # optional: cart currency for mixed-currency carts
cloud="<merchant-token>", # only on the shared platform host
)
# -> {"id": ..., "status": "incomplete", "currency", "line_items", "totals", "ap2": {"merchant_authorization", "nonce"}}
ready = buyer.complete_checkout(cart["id"], cloud="<merchant-token>")
# -> ready_for_complete + payment_handlers[] (pay each challenge on-chain)
order = buyer.get_order(cart["id"], cloud="<merchant-token>") # per-item fulfillment status
Optional AP2 (dev.ucp.shopping.ap2_mandate): pass complete_checkout(id, ap2={"checkout_mandate": ...}, cloud=...) with a buyer-signed SD-JWT-VC for holder-proof authorization. Responses are RFC 9421-signed (ES256) with the merchant key.
Manage your store (full merchant surface)
Beyond priced 402 resources, an agent is a first-class merchant over its own account: full transaction history + actions, multi-store, shop products, landers, payout-info/OTC, discount codes, checkout custom fields, and billing. All key-authenticated; tenant-scoped to the agent.
# Transactions: full history, CSV export, + actions
txs = agent.get_transactions(status="C", limit=50)
csv = agent.download_transactions(date_from="2026-01-01") # raw CSV text
agent.refund_transaction(tx_id) # self-guarding: only if you connected a refund-capable node/coinbase
agent.resend_webhook(tx_id)
agent.submit_hash(tx_id, "0xabc...")
# Stores (sub-merchants)
store = agent.create_store(label="Store B") # api_key+cloud_token+webhook_secret returned once
agent.rotate_store_credential(store["store"]["id"], "cloud_token")
# Shop products, landers, payout-info/OTC
agent.create_shop_product(title="Ebook", price=9.99, currency="USD")
agent.create_lander(checkout_id=flag_id)
agent.create_payout_info(amount=50, currency="USD", email="payee@x.dev") # emails the payee a link
# Discount codes (amount = price multiplier: 0.9 = 10% off) + checkout custom fields
agent.add_discount_code(code="LAUNCH", amount=0.9, checkout_ids="all")
agent.add_custom_field(type="select", label="Size", options=["S", "M", "L"])
# Billing: balances + capacity
bill = agent.get_billing() # {credit_balance, ucash_points, lander_slots:{...}}
agent.buy_lander_pack(10) # debits credit_balance, grows slots
agent.redeem_ucash(1000) # UCASH points -> fee credit
Subagents (scoped RBAC keys)
Delegate a LIMITED credential to another automated principal. A subagent is a scoped sa_ API key with a
staff role: it authenticates against your tenant, but the existing RBAC (uxc_can) enforces a limited
capability set on every endpoint, so it can only do what the role allows. Owner-only (you create them; a
subagent cannot create subagents). Reuses the merchant staff model + seat billing (a subagent with no free
slot debits credit_balance).
# Built-in role: a clerk can create resources + read, but not edit settings, refund, or manage stores
sub = agent.create_subagent(role="clerk", display_name="Fulfillment bot")
# sub["api_key"] is the sa_ key, returned ONCE. The key works on every /v1/* endpoint; writes its role
# lacks return 403, and managing stores stays owner-only.
# Or a custom permission set (slugs: transactions.view, checkouts.edit, payment-links.create, ...).
# store_scope (store ids) restricts it to those stores (empty = all stores).
ro = agent.create_subagent(
role="custom", permissions=["transactions.view", "checkouts.view"], store_scope=[store_id]
)
agent.get_subagents() # list (never returns the api_key)
agent.update_subagent(sub["subagent"]["id"], role="manager", status="suspended")
fresh = agent.rotate_subagent_key(sub["subagent"]["id"]) # old key stops working; new key ONCE
agent.delete_subagent(sub["subagent"]["id"]) # key stops working; seat slot frees
Spend caps (per-caller limits)
Cap how much a single payer can spend on a resource in a rolling window. A payer whose settled spend in the
window reaches amount is refused new authorization (x402 before-charge; on detect the platform refuses to
credit an over-cap payment). Caller identity is the payer wallet (or, with by: 'ip', the buyer IP, enforced at the door before any charge). Optional; off by default.
res = agent.create_resource(amount=0.05, currency="USD", max_per_caller={"amount": 1.00, "window_hours": 24}) # add by="ip" to cap by buyer IP at the door instead of by wallet
agent.set_resource_cap(res["res_id"], {"amount": 0.50, "window_hours": 6}) # change it
agent.set_resource_cap(res["res_id"], None) # clear it
callers = agent.get_resource_callers(res["res_id"]) # [{caller, spend, payments}]
Verifying webhooks
When a payment settles, agents.u.cash POSTs an HMAC-signed event to your webhook URL. Verify it with the
static helper (it runs on your server; no instance or API key needed). The signature is HMAC-SHA256 of
"<t>.<raw_body>" in the X-Webhook-Signature: t=<unix>,v1=<hex> header. Use the raw request body -
re-encoding the JSON breaks the signature.
import json, os
from agents_u_cash import AgentsUCash
# Flask - use the RAW body (request.get_data()), NOT request.get_json():
@app.post("/webhook")
def webhook():
valid = AgentsUCash.verify_webhook_signature(
request.get_data(), # the EXACT raw bytes you received
request.headers.get("X-Webhook-Signature"), # t=<unix>,v1=<hex>
os.environ["UXC_WEBHOOK_SECRET"], # from set_webhook()/rotate_webhook_secret() - shown ONCE
)
if not valid:
return "bad signature", 401
event = json.loads(request.get_data())
# Deduplicate by event["event_id"] (also in X-Webhook-Event-Id) - a settled txn may be delivered >1x.
return "ok", 200
The 300-second replay window is on by default; pass tolerance=0 to skip the freshness check.
Asset codes
accepted_assets takes coin codes (omit it to accept all your configured wallets). Common built-in codes:
| Coin | Code | Note |
|---|---|---|
| Bitcoin | btc |
|
| Bitcoin Lightning | btc_ln |
NOT lightning or btc-ln |
| Ethereum | eth |
|
| EVM L2s | eth_base, eth_arb, eth_op, eth_linea, eth_unichain, eth_world, eth_scroll, eth_ink, eth_abstract, eth_plasma |
plus native mnt, bera, s, mon, hype |
| USDC on Base | usdc_base |
the x402 gasless rail |
| Stablecoins | usdc, usdt, usdt_tron, usdt_bsc |
|
| Others | sol, trx, xrp, ltc, doge, bnb, pol, avax, xmr, algo, bch, dot, xlm, xtz, ucash |
Plus any custom-token code you added via set_custom_token(). A misspelled code raises
AgentsUCashError with .code = 'uxc_unknown_asset' ("Unsupported asset: X"). The live canonical list
grows as chains are added - see agents.u.cash.
Resource vs payment request
Two ways to get paid - pick by use case:
create_resource |
create_payment_request |
|
|---|---|---|
| Lifetime | persistent (payable many times) | one-off link, closes on pay or expiry |
| Door | /r/{res_id} / checkout_url |
/id/<enc> url |
Multi-coin accepted_assets |
yes | no |
Per-caller cap (max_per_caller) |
yes | no |
title / note / expiry / redirect / external_reference |
no | yes |
A resource is a standing price buyers pay repeatedly; a payment request is a single invoice link.
API
| Method | Auth | Description |
|---|---|---|
signup(email, password, primary_wallet=None) |
- | Register; returns api_key |
top_up(amount) |
key | Create a ≥$1 top-up checkout (adds platform credit; activates if not yet) |
get_agent() |
key | Account snapshot (balance, wallets, webhook, earnings summary) |
set_webhook(url) |
key | Set the settlement webhook (auto-generates the HMAC secret; shown once) |
get_webhook() / rotate_webhook_secret() / clear_webhook() |
key | Read (masked) / rotate / clear the webhook |
AgentsUCash.verify_webhook_signature(raw_body, signature_header, secret, tolerance=300) |
- | Verify an incoming webhook's HMAC signature (static; runs on your server) |
set_wallet(asset, address) |
key | Set your receive address for an asset |
set_stripe(secret_key, product_id, webhook_secret, publishable_key=None) |
key | Connect your Stripe account (card rail); verifies the key + product |
get_stripe() |
key | Masked Stripe config + the webhook endpoint to register |
clear_stripe() |
key | Disconnect your Stripe account |
set_custom_token(type, code, contract_address, decimals, name, rate=None, rate_url=None) |
key | Add a custom token (ERC-20/TRC-20/SPL); then set_wallet(asset=code, address=...) to set its receive address |
get_custom_tokens() |
key | List your custom tokens |
delete_custom_token(code) |
key | Remove a custom token |
get_settings() |
key | Read safe settings (confirmations, webhook url+secret, currency, payment prefs, notifications, branding) |
set_settings(partial) |
key | Partially update safe settings |
get_integrations() |
key | Read stored third-party integration credentials (Discord, Telegram, BigCommerce, Ecwid, Wix) |
set_integrations(integrations) |
key | Store third-party integration credentials |
create_resource(amount, currency=None, accepted_assets=None, webhook_url=None) |
key | Create a priced resource |
get_resources(res_id=None) |
key | List resources, or fetch one |
create_challenge(res_id) |
key | Build the multi-coin accepts[] |
verify(challenge_id, hash) |
key optional | Verify + settle (buyer-push: no key needed) |
get_settlements() |
key | Earnings log |
view_door(res_id, html=False) |
- | The public 402 door (JSON, or HTML) |
create_checkout(line_items, currency=None, buyer=None, context=None, cloud=None) |
- | UCP checkout session (multi-item, mixed-currency cart) |
get_checkout(id, cloud=None) |
- | Fetch a checkout session |
complete_checkout(id, ap2=None, cloud=None) |
- | Mint challenges -> ready_for_complete (optional AP2 mandate) |
cancel_checkout(id, cloud=None) |
- | Cancel a checkout session |
get_order(id, cloud=None) |
- | A checkout session as a UCP order (per-item fulfillment) |
search_catalog(query=None, filters=None, pagination=None, cloud=None) |
- | Search the merchant catalog |
get_product(id, cloud=None) |
- | Fetch a single catalog product by id |
lookup_products(ids, cloud=None) |
- | Batch catalog lookup by ids |
get_transactions(...) / get_transaction(id, webhook_log=False) / download_transactions(...) |
key | Full history, one detail, CSV export |
refund_transaction(id) / resend_webhook(id) / submit_hash(id, hash) |
key | Refund (guarded), re-deliver webhook, attach hash |
get_stores() / create_store(...) / update_store(id, ...) / delete_store(id) |
key | Multi-store CRUD |
rotate_store_credential(id, which) / test_store_webhook(id) |
key | Rotate a store credential; send a test webhook |
get_shop_products(id=None) / create_shop_product(**fields) / update_shop_product(id, **fields) / delete_shop_product(id) |
key | Shop products (/v1/checkouts) |
get_landers(...) / create_lander(checkout_id, tpl=None) / update_lander(...) / delete_lander(id) |
key | Landers + offer status |
create_payout_info(...) / get_payout_info(id) / complete_payout(id) |
key | Payout-info / OTC requests |
get_discount_codes() / add_discount_code(code, amount, checkout_ids="all") / set_discount_codes([...]) / delete_discount_code(code) |
key | Discount codes (amount = multiplier) |
get_custom_fields() / add_custom_field(**field) / set_custom_fields(custom_fields, title=None) / delete_custom_field(index) |
key | Checkout custom fields |
get_billing() / buy_lander_pack(qty) / redeem_ucash(amount) |
key | Balances + capacity (lander pack, UCASH redeem) |
All calls return the parsed response dict and raise AgentsUCashError on API errors (which carries .code and .status).
Error handling
All calls return the parsed response dict on success and raise AgentsUCashError on errors; the error
carries .code and .status.
from agents_u_cash import AgentsUCash, AgentsUCashError
try:
agent.create_resource(amount=0.05)
except AgentsUCashError as e:
print(e.code, e.status, e) # e.g. 'uxc_agent_not_activated' 402 ...
Status codes you will see:
| Status | Meaning | SDK behavior |
|---|---|---|
| 200 | success | returns response |
| 202 | pending (x402 verified, awaiting on-chain settlement) | returns response with status: 'pending' (does NOT raise) |
| 402 | payment required / agent not activated | raises, .status = 402 |
| 410 | challenge expired | raises, .code = 'uxc_challenge_expired' |
| 429 | rate limited | raises, .status = 429 |
verify(challenge_id, hash) returns {"settled": True} on success,
{"status": "pending", "confirmations": n, "required": m} while the payment awaits on-chain
confirmation, or {"status": "underpaid"}. An expired challenge raises with .code === 'uxc_challenge_expired'
(HTTP 410 Gone) - catch on .code:
try:
r = buyer.verify(challenge_id, tx_hash)
except AgentsUCashError as e:
if e.code == "uxc_challenge_expired":
... # re-fetch accepts[] and retry
else:
raise
Non-custodial: the platform never holds funds - every payTo is the seller's own wallet, and this client never sees your wallet keys.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agents_u_cash-0.6.6.tar.gz.
File metadata
- Download URL: agents_u_cash-0.6.6.tar.gz
- Upload date:
- Size: 18.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ddd5143f0865ba71aa401c9ae438ffa64e7d739462dd57928c7165babda455be
|
|
| MD5 |
300c8c8e8b43ca5e8dbfd2a7d9ce9400
|
|
| BLAKE2b-256 |
a9b733f2ac992a763783c585eb3db7e310230c3aab7828ee0541e09937ffdbb9
|
File details
Details for the file agents_u_cash-0.6.6-py3-none-any.whl.
File metadata
- Download URL: agents_u_cash-0.6.6-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bf503ca9bc65fae81eeace14cd3a440f7a2f1f3bc7c791bc14e6eae750da375
|
|
| MD5 |
56660432fcc73038e9d8f13356c524f4
|
|
| BLAKE2b-256 |
93417cbde753273a6f7b890ccc76c7135cf83dc4122c5be5120b9a37ca0042a9
|