An AI agent that can buy external tools needs one capability before anything else: the ability to refuse a payment it didn't authorize.
An HTTP 402 Payment Required response can tell the agent what a server accepts. But HTTP 402 is not a receipt. You can inspect the payment requirements for free and decide whether to reject them before connecting a wallet.
This tutorial uses a production x402 v2 endpoint on Base to demonstrate a reproducible, wallet-free preflight. You do not need a login, API key, USDC, or payment.
Disclosure: I work on Skavio LLC. This tutorial uses our own public service and open-source examples. It is not an independent review, and the funded payment branch has not been tested.
1. Request the tool without paying
The following URL checks a public sample transaction hash. It is not a wallet key or secret:
curl -i --max-time 15 \
'https://getskavio.com/x402/v1/usdc/verify?tx_hash=0x98809948204426c574b6f0aabdec03baa996b5c43ce28cc9ca704fa5d07e592e&network=base'
~~~{% endraw %}
The unsigned production request returns **HTTP 402** with a PAYMENT-REQUIRED header. In x402 v2, the header is base64-encoded JSON describing one or more accepted payment offers.
Read that header, not the sales-page price. In our live check on November 6, 2026, the offer was **0.02 USDC** on Base. Production prices and offers can change; a buyer should verify the response on *each* call rather than rely on an old quote.
## 2. Validate the complete offer
A per-request price limit is useful, but it doesn't protect you if a call sends the right amount to the wrong recipient or on the wrong chain.
For this *specific* example, the expected values are:
| Field | Expected value |
|---|---|
| Protocol version | 2 |
| Scheme | exact |
| Network | eip155:8453 (Base mainnet) |
| Asset | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (native Circle USDC on Base) |
| Receiver | 0x62BB64E9Ab0E8104dEf0D7845223580319d4C139 |
| Maximum amount | 20,000 base units = 0.02 USDC |
USDC on Base uses six decimals, so 20,000 divided by 1,000,000 is 0.02.
Below is the core validator from our Node.js example. The important property is **fail closed**: if the scheme, chain, asset, address, amount or number of matching offers changes, the program refuses the request.{% raw %}
~~~javascript
function inspect402(encoded, ceiling = 20_000n) {
if (!encoded) throw new Error("Missing PAYMENT-REQUIRED header");
const requirements = JSON.parse(
Buffer.from(encoded, "base64").toString("utf8")
);
if (requirements.x402Version !== 2 ||
!Array.isArray(requirements.accepts)) {
throw new Error("Unsupported payment protocol");
}
const offers = requirements.accepts.filter(p =>
p.scheme === "exact" &&
p.network === "eip155:8453" &&
String(p.asset || "").toLowerCase() ===
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" &&
String(p.payTo || "").toLowerCase() ===
"0x62bb64e9ab0e8104def0d7845223580319d4c139" &&
/^[0-9]+$/.test(String(p.amount))
);
if (offers.length !== 1) throw new Error("Ambiguous payment terms");
const amount = BigInt(offers[0].amount);
if (amount <= 0n || amount > ceiling) {
throw new Error("Price exceeds authorized cap");
}
return { amount, offer: offers[0] };
}
~~~
This is a policy example, not a universal x402 validator. Your agent must load the correct merchant and allowed token for *each service* from an independently trusted configuration.
## 3. Run a real free quote preview
The full scripts, error handling and unit tests are publicly available:
[**Skavio x402 safe buyer: Python and Node.js**](https://github.com/skavio-eu/skavio-agent-tools/tree/main/examples/x402-safe-buyer).
To try the unpaid mode:
~~~bash
git clone https://github.com/skavio-eu/skavio-agent-tools.git
cd skavio-agent-tools/examples/x402-safe-buyer
python3 preview_x402.py
node skavio-x402.mjs --preview
node --test
~~~
These commands **do not provide wallet credentials or authorize payment**. For this endpoint the preview output looked like:
~~~json
{
"status": 402,
"network": "eip155:8453",
"scheme": "exact",
"priceUSDC": 0.02,
"paid": false
}
~~~
The word to notice is **false**. A price challenge is not a successful purchase.
We verified the real unsigned HTTP 402 with both Python and Node.js. On November 6 our Node test suite passed **3/3**, exercising budget parsing, rejecting invalid or ambiguous payment details and confirming that preview mode does not sign anything. That is meaningful evidence for the **free preflight**, not proof of a funded mainnet transfer.
## 4. Why a single-call cap is not enough
The production risk begins when an agent loops.
If the cap is 0.02 USDC, 1,000 permitted calls could still spend **20 USDC**. A paid implementation also needs:
- A named business task and explicit authorization to buy the tool.
- A cumulative spend budget per task, per day and per wallet.
- A dedicated low-balance wallet, never the company treasury.
- Safe handling of timeouts, idempotent request identities and ambiguous retries.
- A settlement receipt that matches the intended network, token, receiver and amount.
- A manual stop condition and logs suitable for reconciliation.
The GitHub example includes an **explicit opt-in** paid mode backed by the official x402 client libraries. We have verified that the libraries import, but we have **not** executed a funded mainnet call. Do not treat the optional branch as independently settlement-tested.
## 5. Record quote, completion and settlement separately
A robust buyer should distinguish:
| Stage | Evidence | What it does NOT prove |
|---|---|---|
| Quoted | HTTP 402 with a validated offer | No payment occurred |
| Tool completed | Successful endpoint response | Payment settlement might still need verification |
| Paid and verified | Matching receipt and settlement evidence | The returned data still needs validation |
This is especially important if your network times out *after* a wallet tries to pay. Blindly repeating the whole operation with a new ID can create duplicate spending. Store and reconcile the prior attempt first.
## A useful five-minute exercise
Run the free script with the expected network deliberately changed from Base to Ethereum mainnet. It should reject the offer. Repeat with an incorrect receiver or a ceiling below 0.02 USDC. If it *still* proceeds, it isn't enforcing your purchasing policy.
**Takeaway:** A useful buying agent isn't defined by the ability to pay. It is defined by the ability to **refuse, explain and later reconcile** a purchase.
---
**Reproducible code:** [Python/Node x402 buyer with tests](https://github.com/skavio-eu/skavio-agent-tools/tree/main/examples/x402-safe-buyer)
**API specification:** [Skavio x402 docs](https://getskavio.com/x402/docs/)
**Optional no-signup document examples:** [Skavio demo gallery](https://getskavio.com/demo/)
*Those resources are maintained by Skavio LLC and supplied so readers can reproduce the exercise. They are not third-party endorsements.*
Top comments (1)
Good read. On the point that agents must validate payment terms before paying to refuse unauthorized transactions: spending limits stop an agent from overspending, but they don't stop it paying a broken or hijacked endpoint inside the limit. A per-payment check (is this endpoint, price and wallet what it should be?) covers that gap.
(I work on Lumière PayCheck, which checks x402 endpoints before agents pay them.)