Check every payment before your agent signs it
Guard compares each payment request with what we measured for that seller, then warns or blocks by your rules.
What it checks
- Payout address seen before
- Price above what we last measured
- Our last check is recent
- Request names the host you called
Runs inside your agent. Only the seller’s host name reaches us.
Try it
Example facts for seller.example. No wallet, nothing is paid. Example
The seller asks for
Your rule
Allowed with 1 warning
The price is above what we last measured on this route. The payment goes ahead; the warning reaches your logs.
Reason code: price_above_observed (warn) · Guard: createPrePaymentGuard()
For developers
Install (npm + PyPI 0.2.1)
npm install @botsrated/x402-preflight
pip install botsrated
@x402/fetch
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { createPrePaymentGuard } from "@botsrated/x402-preflight";
const client = new x402Client();
registerExactEvmScheme(client, { signer });
const guard = createPrePaymentGuard(); // default: warns, never blocks on payTo
client.onBeforePaymentCreation(guard.beforePaymentCreation);
const paidFetch = guard.wrapFetch(wrapFetchWithPayment(fetch, client)); // tells the guard which URL is paid
@x402/mcp (paid MCP tool calls)
import { createx402MCPClient } from "@x402/mcp";
import { createPrePaymentGuard, mcpPaymentApprover } from "@botsrated/x402-preflight";
const guard = createPrePaymentGuard();
const mcp = createx402MCPClient({ /* ... */
onPaymentRequested: mcpPaymentApprover(guard, "https://seller.example/mcp") });
Python
from botsrated import STRICT_GUARD_POLICY, PrePaymentGuard
guard = PrePaymentGuard(policy=STRICT_GUARD_POLICY) # the policy is your choice
client.on_before_payment_creation(guard.x402_hook) # x402 Python client
get = guard.wrap_get(x402_requests(client).get) # tells the guard which URL is paid
Agent frameworks
| Every stack | LangChain, OpenAI Agents SDK, LangGraph, CrewAI, Vercel AI SDK, Mastra and more, three steps each. Every stack → |
| Any MCP client | https://botsrated.com/mcp (free tool preflight) |
Source and README: npm, PyPI. Reference: /docs.
Every reason code
Reason code | When | Default | strictGuardPolicy |
|---|---|---|---|
payto_not_observed | The payTo is not among the payTos our probes recorded for the host (same network). | warn | block |
payto_invalid | The payTo is not a 20-byte hex address. | warn | block |
price_above_observed | Same route, amount above the last amount we recorded. | warn | warn |
resource_url_mismatch | The 402's resource.url names another host than the URL being paid (when the guard knows that URL). | block | block |
no_request_url | The URL being paid is unknown (no guard.wrapFetch / forRequest). | off | block |
data_stale | Our latest probe of the host is older than 24 hours. | info | info |
not_covered | We have no probe record for the host. Says nothing about the seller. | info | info |
low_valid_402_rate | Recent valid-402 rate below your minValid402Rate (only when you set one, 3+ recent probes). | off | off |
Fail-open by default
If BotsRated is unreachable or the guard errors, the payment goes ahead (failOpen: true). resource_url_mismatch still blocks. Stricter setup:
import { createPrePaymentGuard, strictGuardPolicy } from "@botsrated/x402-preflight";
const guard = createPrePaymentGuard({
policy: { ...strictGuardPolicy, priceAboveObserved: "block", minValid402Rate: 0.8 },
failOpen: false, // stop the payment if BotsRated can't be reached
paidCheckAtOrAboveUsd: 0.25, // paid check ($0.001 after the free daily ones) for bigger payments
onDecision: (d) => console.log(d.action, d.reasons),
});
Sandbox JSON
Quote in the 402
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "2000",
"payTo": "0x1111111111111111111111111111111111111111",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
Decision
{
"action": "allow",
"host": "seller.example",
"reasons": [
{
"code": "price_above_observed",
"detail": "the quoted amount (2000 atomic units) is above the last amount BotsRated recorded on this route (1000).",
"effect": "warn"
}
],
"facts": {
"covered": true,
"host": "seller.example",
"knownPayTos": [
"0x1111111111111111111111111111111111111111"
],
"observedNetwork": "eip155:8453",
"probedUrl": "https://seller.example/api/search",
"observedAmount": "1000",
"stale": false,
"receiptUrl": "https://botsrated.com/receipt/example-receipt-id",
"source": "preflight"
},
"disputeUrl": "https://botsrated.com/disputes"
}Questions
Can Guard move money?
No. It only advises your own x402 client. It can’t sign or send a payment.
What does BotsRated see?
Only the seller’s host name. Never the path, wallet, amount or payment header.
Is a block a judgment of the seller?
No. It applies your rule to what we measured, with a link to the receipt.
What if BotsRated is down?
By default the payment goes ahead (fail-open). You can switch that off.