Your agent costs real money to run — LLM tokens, compute, a paid API behind the scenes. You want callers to chip in before the work happens, programmatically, without invoices or dashboards or human approval. Bindu plugs into x402, an open protocol that revives the long-dormantDocumentation Index
Fetch the complete documentation index at: https://docs.getbindu.com/llms.txt
Use this file to discover all available pages before exploring further.
HTTP 402 Payment Required status for machine payments. A caller hits your agent; if they haven’t paid, you reply 402 with exactly what’s owed. They sign a one-shot authorization with their wallet, retry with an X-PAYMENT header, and your handler runs only after a facilitator has verified the payment on-chain.
Why HTTP 402
| Without payment gating | With Bindu x402 |
|---|---|
| Anyone hits your handler; you eat the compute | Callers must prove payment before the handler runs |
| Billing depends on dashboards, webhooks, accounts | Settlement is a signed EIP-3009 authorization on a blockchain |
| Per-call micropayments are impractical | Per-call micropayments are native (USDC scales to 6 decimals) |
| Replay protection is your problem | Bindu rejects replayed nonces before settlement |
| Hard to compose into autonomous A2A workflows | Built into the A2A request path; agents can pay agents |
| Coupled to a centralized provider | Facilitator is pluggable; chains/tokens are operator-configurable |
x402 is for per-request micropayments between machines — a few cents
per call, on the request hot path. It is not a replacement for subscriptions,
marketplaces, or human checkout flows. Different problem, different tool.
The Protocol Flow
Three trips, four moving parts:- Caller — anything that speaks HTTP and can sign with an EVM wallet.
- Your Bindu agent — gatekeeper. Tells callers what it costs, accepts proof, runs the handler.
- Facilitator — separate HTTP service. Reads the signature, checks the chain, broadcasts the transfer. Bindu never speaks to the blockchain directly; it trusts the facilitator’s yes/no.
- Blockchain — where USDC actually moves.
402 + retry
Standard HTTP. No SDK lock-in for the caller — just a header and a body.
Verified on-chain
Facilitator recovers EIP-3009 signatures and checks balance before the handler runs.
Replay-safe
Nonces are claimed in Redis (or in-memory) before settlement. Re-sent payloads bounce.
Turning Payments On
Add one block to yourbindufy config — a price, a token, a network, an address:
message/send without payment now gets a 402 describing exactly what’s required.
By default only
message/send is gated. To gate more methods set the
environment variable X402__PROTECTED_METHODS or edit
app_settings.x402.protected_methods. The default is intentionally narrow —
you don’t want to charge for tasks/get polling.Multiple payment options
Makeexecution_cost a list and the 402 response advertises all of them. The caller picks one:
What 402 Actually Looks Like
When a caller hits a protected method withoutX-PAYMENT, Bindu replies with the v2 wire format from x402.PaymentRequired:
"0.01"from your config →"10000"on the wire (atomic units; USDC has 6 decimals)."base-sepolia"from your config →"eip155:84532"(CAIP-2 chain ID; the x402 v2 SDK keys everything off CAIP-2 strings internally).extra.name/extra.versionpopulate the EIP-712 domain the caller must sign over — must match the on-chain token’s domain separator or signature recovery fails.
agent block is Bindu-specific (not part of the x402 spec) — it lets callers discover the agent card without a second round-trip.
What the caller sends back
The caller builds an EIP-3009TransferWithAuthorization, signs it with their wallet, wraps it in the v2 payload envelope, and base64-encodes the JSON:
X-PAYMENT header on the retry.
v1 payloads are rejected outright. The middleware logs
"x402 v1 payment payloads are no longer accepted; please re-sign with v2"
and returns 402. Re-sign with a v2 client.End-to-End Flow
Unpaid request
Caller fires a
message/send with no X-PAYMENT. Middleware replies 402
with the accepts block above.Sign + retry
Caller signs an EIP-3009 authorization with their wallet, base64-encodes
the v2 payload, sends it as The middleware now:
X-PAYMENT.- Decodes the base64 + parses the v2 payload.
- Matches it against your
execution_costrequirements (find_matching_requirements). - Claims
(network, asset, nonce)in the nonce store — before any facilitator round-trip. Replays bounce here. - Calls the facilitator’s
/verify— signature recovery + on-chainbalanceOf. Trustsresult.is_valid. No fall-through. - Hands the request off to your handler with
request.state.payerset.
Handler runs, settlement at completion
Your handler executes. When the task finishes, the worker calls the
facilitator’s The
/settle endpoint, which broadcasts the
transferWithAuthorization on-chain. The receipt lands on the task:metadata block is your audit trail. x402.payment.status and
x402.payment.receipts are the keys to query later if you ever need to
reconcile, dispute, or refund.Browser-Capture Flow (for non-agent callers)
Sometimes the caller is a human, not another agent — and a human wants to click a MetaMask popup, not generate an EIP-3009 signature in Python. Bindu ships three endpoints that wrap the same x402 machinery in a browser-friendly flow:| Endpoint | Method | Purpose |
|---|---|---|
/api/start-payment-session | POST | Mint a session ID + browser_url. |
/payment-capture?session_id=... | GET | Paywall page; renders MetaMask flow; captures the signed token. |
/api/payment-status/{session_id} | GET | Poll for the captured payment_token (base64). |
Sessions expire after 15 minutes by default (see
PaymentSessionManager(session_timeout_minutes=15)). The token returned by
/api/payment-status/... is the same base64 payload the middleware expects
— Bindu does not consume it when you poll, so you can pass it on to
the actual message/send call.Facilitators and Networks
Bindu doesn’t talk to chains directly. It talks to a facilitator — an HTTP service that implements/supported, /verify, /settle per the x402 spec. The facilitator is what dictates which chains and tokens actually work.
Default facilitator (Coinbase)
Default facilitator (Coinbase)
https://x402.org/facilitator — Coinbase-operated, this is what Bindu uses
out of the box. Supports Base mainnet, Base Sepolia, plus some non-EVM
chains (Solana, Algorand, Aptos, Stellar) via x402’s broader scheme set.Does not support SKALE, Polygon, Avalanche, Ethereum mainnet, or
arbitrary L2s. If you need those, you need a different facilitator.Swapping facilitators
Swapping facilitators
Single environment variable:Bindu picks it up at startup and wires the
HTTPFacilitatorClient(FacilitatorConfig(url=...)) into the
x402ResourceServer. Restart the agent for the change to take effect.Adding non-default EVM chains
Adding non-default EVM chains
The x402 v2 SDK only ships built-in asset metadata for Base mainnet
(
eip155:8453) and Base Sepolia (eip155:84532). For any other EVM
chain, you need to declare two things:- A facilitator that supports it (set via
X402__FACILITATOR_URL). - The USDC contract on that chain, so Bindu can convert a
human-readable price like
"$0.01"into atomic units of the right ERC-20.
app_settings.x402.extra_networks:asset_name and asset_eip712_version go straight into the EIP-712
domain the caller signs over — they must match the on-chain token’s
name() and domain version, or signature recovery on the facilitator
side fails.With that registered, your agent config uses the friendly slug:Built-in network slugs
Built-in network slugs
Bindu translates these friendly names to CAIP-2 automatically:
Anything else gets passed through verbatim — useful if you already speak
CAIP-2.
| Slug | CAIP-2 |
|---|---|
base-sepolia | eip155:84532 |
base, base-mainnet | eip155:8453 |
ethereum, ethereum-mainnet | eip155:1 |
ethereum-sepolia | eip155:11155111 |
| (operator-defined) | from extra_networks[...].caip2 |
Configuration Reference
All settings live onapp_settings.x402 (env prefix X402__):
| Setting | Env var | Default | Purpose |
|---|---|---|---|
facilitator_url | X402__FACILITATOR_URL | https://x402.org/facilitator | Where /verify and /settle are called. |
default_network | X402__DEFAULT_NETWORK | base-sepolia | Used when execution_cost.network is missing. |
pay_to_env | X402__PAY_TO_ENV | X402_PAY_TO | Env var name for the recipient wallet (fallback). |
max_timeout_seconds | X402__MAX_TIMEOUT_SECONDS | 600 | Hard ceiling on authorization windows. |
extension_uri | X402__EXTENSION_URI | https://github.com/google-a2a/a2a-x402/v0.1 | A2A extension URI advertised on the agent card. |
protected_methods | X402__PROTECTED_METHODS | ["message/send"] | JSON-RPC methods that demand payment. |
extra_networks | (Python only) | { "skale-europa": ... } | Operator-supplied EVM chain registrations. |
Per-requirement
max_timeout_seconds is hard-coded to 60 in
_create_payment_requirements — that’s the EIP-3009 authorization window
Bindu advertises in the 402. app_settings.x402.max_timeout_seconds is the
outer ceiling, not the per-request value.Replay Protection
Without server-side dedupe, a caller who saw one valid payment could replay it within thevalidBefore window and get unlimited work for one payment. Bindu closes this by claiming (network, asset, nonce) in a nonce store before calling the facilitator:
| Backend | When | TTL |
|---|---|---|
InMemoryNonceStore | No Redis configured | max_timeout_seconds + 60s buffer |
RedisNonceStore | app_settings.scheduler.redis_url set | Same, via SET NX EX (atomic) |
402 { "error": "Payment nonce already used (replay)" } and never reaches the facilitator. The buffer (NONCE_TTL_BUFFER_SECONDS = 60) keeps the dedupe key alive a bit past the EIP-3009 validBefore so clock skew or a slow settlement doesn’t free the slot prematurely.
How the Middleware Decides
Pipeline on every request to the protected path (/, POST):
- Parse JSON-RPC body. Unparseable →
402 { "error": "Malformed JSON-RPC body" }. (A previousexcept Exceptionhere let bad bodies fall through to the handler unpaid; that’s fixed.) - Method check. If the method isn’t in
protected_methods, the request flows straight through unpaid. X-PAYMENTpresent? Missing → 402.- Decode + parse v2 payload. Bad base64 or bad JSON → 402.
- Match a requirement. No match →
402 { "error": "No matching payment requirements found" }. - Claim the nonce. Already used →
402 { "error": "Payment nonce already used (replay)" }. Store error → fail closed. - Facilitator
/verify.isValid: falseor exception → 402 withInvalid payment: <reason>orPayment verification failed. - Handler runs. On task completion, the worker calls
/settleand stamps the receipt onto task metadata.
Going to Production
Develop on testnet
Use
base-sepolia and the Coinbase facilitator. Get testnet ETH from a
Base Sepolia faucet for gas; get testnet USDC from the Base Sepolia USDC
faucet. Run the full flow against your own agent until the happy path is
boring.Flip one config line
Change
"network": "base-sepolia" to "network": "base". Same code,
same wallet shape, same payload shape — real USDC on Base mainnet.Configure Redis
Set
app_settings.scheduler.redis_url. Without it, replay protection is
per-process and breaks under horizontal scale.Pick prices that match your costs
A penny per call sounds reasonable until an LLM call costs you five
cents. Math out provider cost vs
amount before you ship. Each new task
needs a new payment — a finished task doesn’t grant credit to the next
one.Calling a Paid Agent from Python
Once you’ve got a base64X-PAYMENT (either signed yourself or fetched from /api/payment-status/...), the actual call is unremarkable:
hermes_agent example in the Bindu repo — same EIP-3009 flow works on any EVM chain.
Troubleshooting
Caller keeps getting 402 forever
Caller keeps getting 402 forever
The
X-PAYMENT header is missing, malformed, or doesn’t match any
advertised requirement. The 402 body tells you exactly which (error
field)."Payment verification failed"
"Payment verification failed"
The facilitator’s
/verify raised or returned isValid: false. Either
the signature is wrong, the chain is wrong, or the facilitator doesn’t
know the chain you asked for. Check the agent’s server logs for the
facilitator’s invalid_reason."Payment nonce already used (replay)"
"Payment nonce already used (replay)"
Caller is sending the same authorization twice. They need to sign a
fresh one with a new nonce for every request.
"No matching payment requirements found"
"No matching payment requirements found"
The
accepted block in the payload doesn’t match anything in your
execution_cost. Usually a network or asset address mismatch — confirm
the caller signed for the same network and asset you advertised."x402 v1 payment payloads are no longer accepted"
"x402 v1 payment payloads are no longer accepted"
The caller’s library is producing v1 payloads. v2 verification on the
Bindu side rejects them; re-sign with a v2 client.
Related
- x402 Protocol — the open standard everything here is built on.
- Base documentation — what you’ll be paying on by default.
- Decentralized Identifiers (DIDs) — pair payment with peer identity for end-to-end accountability.