New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

settled-x402

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

settled-x402

Don't let your agent pay an x402 endpoint that doesn't deliver. Checks each quote against Settled's independent evidence (paid test purchases, payTo, payee, buyer reports) before it is signed. Works with @x402/fetch, x402-fetch, any x402Client and Coinbas

latest
npmnpm
Version
0.3.0
Version published
Maintainers
1
Created
Source

settled-x402

Check an x402 quote against independent evidence before your agent signs it.

settled-x402 asks Settled about every x402 payment before your agent signs it. Settled is an independent x402 index. It probes endpoints itself, buys from them with real USDC, and checks what comes back against each listing. For the payment in hand, the guard checks:

  • The payTo. Is it the one Settled's own probe gets from the same URL at about the same time? A quote that pays someone else is how a spoofed endpoint, or a man in the middle, gets paid.
  • Delivery. Did Settled's scout get real content back when it paid this endpoint, and did that content match the listing? The settlement transaction comes with the answer.
  • The seller. Has the payTo ever been used on Base? Does the seller run a clone farm? What did paying agents report?
  • The price. Is it higher than the one Settled was quoted?

Payments with a verdict you block, avoid by default, are stopped before anything is signed.

npm install settled-x402

Node 20 or later, no dependencies, ES modules (require() works on Node 20.19+ and 22.12+).

Set it up with @x402/fetch or @x402/axios

Wrap your x402 client in an x402HTTPClient and guard that. The guard then knows both the URL you requested and the exact option your client is about to pay:

import { x402Client, x402HTTPClient, wrapFetchWithPayment } from '@x402/fetch';
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { privateKeyToAccount } from 'viem/accounts';
import { settledGuard } from 'settled-x402';

const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.PRIVATE_KEY) });

const httpClient = settledGuard(new x402HTTPClient(client));
const fetchWithPayment = wrapFetchWithPayment(fetch, httpClient);

wrapAxiosWithPayment(axios.create(), httpClient) works the same way.

When the guard blocks a payment, nothing is signed. The call rejects with an error whose message contains Payment creation aborted: Settled: avoid for <url>: <reasons>.

You can also guard the plain x402Client with settledGuard(client). The guard then sees only the URL the 402 names (resource.url), which the seller writes. It still checks that URL's payTo against Settled. But a seller can leave the URL out and get unknown, which isn't blocked unless you block unknown. Prefer the x402HTTPClient form.

With x402-fetch (v1) or any payment wrapper that takes a fetch

import { wrapFetchWithPayment } from 'x402-fetch';
import { withSettled } from 'settled-x402';

const fetchWithPayment = wrapFetchWithPayment(withSettled(fetch, { network: 'base' }), walletClient);

withSettled sits under the payment wrapper. When an endpoint answers 402 with an x402 quote, the quote is checked before the wrapper sees it. A blocked verdict throws SettledBlockedError, whose error.result holds the full result, so the wrapper never signs.

withSettled can't see which option the wrapper will pick, so it judges every option in the quote, from the header and from the body, and reports the riskiest. Set network to the networks your wallet pays on so that options on other chains don't count.

With Coinbase AgentKit

AgentKit's x402 actions build their own x402 client and pay through the global fetch, so there is no client to hand to settledGuard. Guard the global fetch instead, and give the agent Settled's actions:

npm install settled-x402 @coinbase/agentkit
import { AgentKit, x402ActionProvider } from '@coinbase/agentkit';
import { guardGlobalFetch } from 'settled-x402';
import { settledActionProvider } from 'settled-x402/agentkit';

guardGlobalFetch({ network: 'base' }); // the network your wallet pays on

const agentkit = await AgentKit.from({
  walletProvider,
  actionProviders: [x402ActionProvider(), settledActionProvider()],
});

guardGlobalFetch(options) puts withSettled under every fetch in the process. When make_http_request_with_x402 or retry_http_request_with_x402 gets a quote with a blocked verdict, the action fails with Settled: avoid for <url>: <reasons> before anything is signed. make_http_request fails the same way, so the agent finds out before it asks to pay. Anything that isn't a 402 with an x402 quote passes through untouched. Calling it again changes nothing, and the function it returns puts the original fetch back.

settledActionProvider(options) adds two actions. Neither pays anything.

ActionArgumentsReturns
SettledActionProvider_check_x402_endpointurl, method (GET or POST)The endpoint's quote, read without paying, with Settled's verdict and reasons, judged on the wallet's network
SettledActionProvider_find_x402_endpointsquery, maxPriceUsd, limitLive endpoints on the wallet's network, with price, quality score and whether Settled's test purchase delivered

It takes pass (or reads SETTLED_PASS from the environment), network, timeoutMs, skipHosts, includeQuery and baseUrl. Without a pass, both actions count against the free 300 calls a day.

Discovery

AgentKit's discover_x402_services reads a facilitator's /discovery/resources list. Settled serves that format at https://settled.tools/discovery/resources, limited to endpoints it has checked: live, priced in USDC, probed in the last 3 days, and not failing Settled's last test purchase. Endpoints Settled has bought from successfully come first, and each item's metadata.settled holds the evidence.

  • AgentKit 0.10 takes any URL as the facilitator. Tell the agent to use https://settled.tools.
  • Unreleased AgentKit (its main branch) only takes facilitators you register: x402ActionProvider({ registeredFacilitators: { settled: 'https://settled.tools' } }). The agent then asks for settled.

Check one endpoint

import { check } from 'settled-x402';

const result = await check('https://api.example.com/paid', { quote: paymentRequired, network: 'base' });
result.verdict; // 'pay' | 'caution' | 'avoid' | 'unknown' | 'skipped'
result.reasons; // why, in plain words
result.options; // each option in the quote and how its payTo compares with Settled's record
result.details; // link to Settled's full answer

Without a quote, the guard can only report Settled's record for the URL. To have the quote read for you, use inspect. It requests the endpoint without paying (GET, or POST with { method: 'POST' }) and checks the 402 it gets back:

import { inspect } from 'settled-x402';

const result = await inspect('https://api.example.com/paid', { network: 'base' });
result.endpoint; // { url, httpStatus, error }: what the unpaid request got

From the command line

$ npx settled-x402 check https://api.bitrefill.com/x402/gift-cards/search
PAY · https://api.bitrefill.com/x402/gift-cards/search
  quote     $0.002 on base to 0x480C…846A (4 options)
  options   base: $0.002 to 0x480C…846A · payTo matches Settled
            (arbitrum: $0.002 to 0x480C…846A · same address as on Settled's network)
            (polygon: $0.002 to 0x480C…846A · same address as on Settled's network)
            (solana: $0.002 to 5yASLj…NgRn · not on record at Settled)
            the verdict covers the options on base; pick another with --network
  Settled   live, delivered · $0.002 on base to 0x480C…846A · scout's last purchase 2026-10-01 (tx 0x62cd…5a29)
  why       payTo matches what Settled sees for this endpoint (0x480C…846A)
            a real payment by Settled's scout came back with content, tx 0x62cd…5a29
  details   https://settled.tools/v1/free/check?url=https%3A%2F%2Fapi.bitrefill.com%2Fx402%2Fgift-cards%2Fsearch

The command reads the endpoint's 402 quote without paying, then asks Settled about it.

  • Network: by default the verdict covers the options on the network Settled has on record. Use --network solana to judge another.
  • Exit codes: 0 pay, 2 caution, 3 avoid, 4 unknown, 1 usage or internal error.
  • Other flags: --json prints the full result; --post reads a POST endpoint's quote.

Settled's MCP server over stdio

For Claude Desktop, Cursor and other clients that start MCP servers as a command:

{
  "mcpServers": {
    "settled": { "command": "npx", "args": ["-y", "settled-x402", "mcp"] }
  }
}

Once you hold a day pass, add "env": { "SETTLED_PASS": "<pass>" }. Clients that support remote MCP servers can connect to https://settled.tools/mcp directly. In Claude Code that's claude mcp add --transport http settled https://settled.tools/mcp.

Give your agent the skill

The package ships the settled-x402 Agent Skill: the same rules as the guard, written for an agent that reads instructions rather than calling code. It tells the agent to run Settled's free check before any x402 payment it hasn't made successfully before, walk a short decision table (sanctioned payee, seller on hold, dead status, payTo mismatch with the quote in hand, price drift, verified delivery, quality), preflight when the money matters, report afterwards, and say why. Three files: SKILL.md, references/api.md and scripts/settled_check.py (standard-library Python, prints a one-line decision).

npx settled-x402 skill install            # copies it into ./.claude/skills/settled-x402
npx settled-x402 skill install --global   # into ~/.claude/skills, for every project
npx settled-x402 skill install --to <dir> # into any skills folder
npx settled-x402 skill                    # prints where the files are: node_modules/settled-x402/skills/settled-x402
npx settled-x402 skill show               # prints SKILL.md

Claude Code and the Claude Agent SDK load skills from .claude/skills/; Claude Managed Agents, OpenClaw and other harnesses take the folder or the files. The skill expects the Settled MCP server or plain HTTP (GET https://settled.tools/v1/free/check?url=...), so it works without this package's code too. Claude Code users can instead install the plugin, which adds the skill and the MCP server together: /plugin marketplace add justinedwardvolmer/settled-skill, then /plugin install settled@settled.

Verdicts

VerdictWhat it meansBy default
payThe payTo matches what Settled sees, and nothing on record is against itpaid
cautionWorth a look, not damning. Examples: a price above what Settled was quoted, a different address on another EVM chain, no on-chain history for the payTo, a paid call that didn't match its listing, failing probespaid
avoidStrong evidence against paying: a payTo on Settled's network and asset that isn't the one Settled sees, a scout payment that came back empty, a seller on hold (it took the scout's payment twice in 30 days and delivered nothing), a clone farm, or most paying agents reporting failure. With a pass it also covers honeypot listings and circular paymentsblocked
unknownNothing to judge by. Settled has no quote for this network or URL, can't be reached, or your free checks are used uppaid
skippedNot sent to Settled: payments to settled.tools itself, skipHosts, and local or private addressespaid, never blocked

To block more, pass block, for example settledGuard(httpClient, { block: ['avoid', 'caution'] }). Add 'unknown' to fail closed when Settled can't vouch for a payment.

What the payTo check can and can't catch

The guard compares the option you're paying with what Settled's prober gets from the same URL at about the same time. Settled reuses a probe that is under 5 minutes old; otherwise it probes while you wait.

It catches:

  • A quote that pays someone other than the one Settled sees on the same network, in any asset, including a different address hidden next to the right one.
  • A quote whose header and body disagree.
  • A 402 that claims to be a settled.tools route but pays someone else.

It can't catch:

  • The endpoint itself switching its payTo for everyone. Settled sees the switch too. The scout's receipts were for the old address.
  • Options on networks Settled has no quote for. Settled records the cheapest option of each endpoint. On other EVM chains the guard compares addresses: a different address is caution, because honest sellers do this. In a sample of 385 live x402 quotes, 13 of the 93 sellers offering several EVM chains used a different address on one of them. On other chains, such as Solana when Settled recorded Base, the payTo can't be compared, so the verdict is unknown.

A seller that honestly uses different addresses for different assets on one network will get avoid for the asset Settled didn't record.

After a seller legitimately changes its payTo, payments can be blocked for up to about 10 minutes, until both caches refresh.

Options

settledGuard(client, options), withSettled(fetch, options), guardGlobalFetch(options), check(url, options) and inspect(url, options) all take:

OptionDefault
block['avoid']Verdicts that stop the payment
passnoneA Settled day pass: switches to /v1/preflight and removes the daily cap
networkallNetworks your wallet pays on, such as 'base' or ['base', 'eip155:137'], or 'auto' for the one Settled has on record. Used only when the selected option isn't known (withSettled, check). Without it, an endpoint that also offers a chain Settled has no quote for, like Bitrefill's Solana option, comes out unknown
onVerdictnoneCalled with every result, blocked or not. It isn't awaited
timeoutMs8000How long to wait for Settled. A timeout gives unknown
cacheTtlMs300000How long an answer is reused for the same URL
skipHosts[]Hosts never sent to Settled, such as 'api.mycompany.com' or '*.mycompany.com'
includeQueryfalseAlso send the URL's query string to Settled
allowPrivateHostsfalseAlso send localhost and private-network URLs. Only useful with your own baseUrl
fetchglobalThis.fetchThe fetch used to reach Settled. Pass one that uses your proxy if your agent needs one
baseUrlhttps://settled.tools

inspect also takes method ('GET' or 'POST'), body (a string; default '{}' for POST) and quoteTimeoutMs (default 15000).

Free, or with a day pass

Without a pass, the guard uses Settled's free check:

  • 300 checks a day per client, counted by IP address and reset at 00:00 UTC.
  • The 300 are shared with Settled's free MCP tools and /v1/free/endpoints.
  • At most 30 checks a minute.

When the free checks run out, verdicts are unknown and payments go through unless you block unknown.

A day pass costs $0.05 in USDC over x402. It lasts 24 hours and allows 120 requests a minute with no daily cap. It switches the guard to Settled's preflight, which adds seller settlement-integrity and honeypot checks. Buy one with the client you already have:

const res = await fetchWithPayment('https://settled.tools/v1/pass', { method: 'POST' });
const { pass } = await res.json();
const guarded = settledGuard(new x402HTTPClient(client), { pass });

What is sent to Settled

Sent: only the origin and path of the endpoint you're about to pay, with no query string, fragment or credentials, plus your IP address as with any request. Your keys, the payment and your request body are never sent. Local and private-network addresses are never sent.

The AgentKit search action also sends its keyword, network and price limit. inspect and the AgentKit check action read the quote from the endpoint directly, not through Settled.

Added to Settled's public index: an endpoint Settled hasn't seen before. Settled probes it itself, then adds it. Put the hosts of endpoints you'd rather keep out in skipHosts.

Signed: Settled's answers are signed (EIP-191). result.answer holds the answer, which you can verify at https://settled.tools/v1/verify or with any EVM library.

Limits of the evidence

  • Delivery: Settled's scout only buys endpoints priced at $0.05 or less, and at most about once a week each. Pricier endpoints have no delivery evidence.
  • Not advice: the data is observational and best-effort, and it is not financial advice. If you think a verdict is wrong, tell @settledfyi.

License

MIT

Keywords

x402

FAQs

Package last updated on 07 Oct 2026

Related posts