---
title: "x402 API sellers: test your paid endpoints | Settlens"
description: "Check that your x402 paid API returns a valid 402 challenge, binds each payment to the right URL, recipient and price, fails closed and stays that way."
url: https://settlens.com/use-cases/api-sellers
last_updated: 2026-10-13
---

# Prove your x402 API charges the right terms

> **In short:** Settlens checks an x402 paid API the way a paying client sees it: a valid 402 challenge, payments bound to the requested URL, terms that match your approved recipient, asset, network and price, and, with your authorization, a resource that refuses an invalid payment. It runs before launch, in CI and on a schedule.

For teams that sell API access through an x402 resource server. Settlens checks the 402 challenge your endpoint returns, the terms it advertises and what it does with an invalid payment: before launch, on every pull request and in production.

[Request a pilot](https://settlens.com/contact) [See what can go wrong](https://settlens.com/use-cases/api-sellers#failure-modes)

Real output from the bundled demo target, which advertises 0.01 USDC (10000 atomic units) on Base Sepolia.

**Passive scan with expected terms**

```text
$ settlens scan http://127.0.0.1:4402/secure \
    --expect-scheme exact --expect-network eip155:84532 \
    --expect-pay-to 0x209693Bc6afc0C5328bA36FaF03C514EF312287C \
    --max-amount 10000 --no-write
Settlens x402 assessment 8ac68b3f
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/secure
17 pass · 0 fail · 0 warn · 0 skip
 
Total: 17 pass · 0 fail · 0 warn · 0 skip
```

## Who this is for

You run an HTTP API, data feed or content endpoint and charge per request with [x402](https://settlens.com/learn/what-is-x402). A request without payment gets HTTP 402 and a `PAYMENT-REQUIRED` header; the client (a script, an app or an AI agent) signs a payment and retries with a `PAYMENT-SIGNATURE` header. Your middleware then asks a facilitator to verify and settle it.

Between a request and your revenue sit your payment middleware, your pricing configuration, your proxies and your facilitator. Any of them can change what you advertise or what you serve without anyone noticing. Settlens checks that chain from the outside, the way a paying client sees it.

Settlens assesses x402 V2. If your middleware still uses the V1 `X-PAYMENT` flow, the [V1 to V2 migration guide](https://settlens.com/learn/x402-v1-to-v2-migration) lists what changes. To look at one challenge right away, paste your `PAYMENT-REQUIRED` header into the free [x402 payment request checker](https://settlens.com/tools/x402-checker).

## What goes wrong on a paid endpoint

Each failure mode maps to a check Settlens runs. The tag on each card is the check id you will see in reports.

- **Content is served for an invalid payment**

  Middleware that trusts the presence of a PAYMENT-SIGNATURE header instead of a verified one gives the resource away. The authorized active probe sends one invalid signature and fails critical on any 2xx answer.

  security.invalid_signature_fail_closed

- **The challenge cannot be parsed**

  A missing header, a value that is not Base64 JSON or an object the official @x402/core SDK rejects leaves paying clients unable to read your terms.

  schema.official_x402_sdk

- **Payment is bound to another resource**

  A proxy, a rewritten host or a staging URL in production makes resource.url differ from the URL that was requested, so a payment can be steered to or reused for another resource.

  binding.resource_url

- **Terms change between identical requests**

  Two identical unauthenticated requests should return the same resource, accepts and extensions. A price or recipient that moves between them is flagged.

  stability.payment_requirements

- **Advertised terms leave your policy**

  A deploy that changes the recipient, the token, the network or the scheme, or lifts the amount above your ceiling, fails the matching policy check. In a scan, every advertised option must fit the policy.

  policy.recipient

- **Transport weakens the challenge**

  Plain HTTP lets a network attacker rewrite payTo or the amount. A redirect moves the challenge away from the assessed URL; Settlens never follows it and flags it.

  transport.https

- **Authorization windows are too long**

  maxTimeoutSeconds above 3600 keeps a signed payment usable for longer than settlement needs, which widens replay and misuse exposure.

  freshness.timeout_window

- **Your facilitator cannot settle what you sell**

  When you name a facilitator, every scheme and network you advertise must appear in its GET /supported kinds, or clients sign payments that can never be verified.

  facilitator.requirement_coverage

## The Settlens workflow for API sellers

Four stages reuse the same checks. Start with a passive scan; add the others as the endpoint moves toward production.

1. **Scan the terms you mean to advertise**

   List the schemes, CAIP-2 networks, recipients (`payTo`), token `assets` and maximum atomic amount in a [configuration file](https://settlens.com/docs/configuration). `settlens check` sends two unauthenticated requests per target by default, follows no redirect and fails a policy check whenever an advertised option falls outside it.

2. **Prove it fails closed on staging**

   With your approval reference, `--active --authorized --authorization-ref` sends one intentionally invalid `PAYMENT-SIGNATURE`, and the same payload to your facilitator's `/verify` when one is configured. Settlens never calls `/settle`.

3. **Gate every pull request**

   The GitHub composite Action runs the same configuration, fails the job at your `fail-on` threshold (default `high`) and writes JSON, HTML and SARIF reports. See the [x402 CI gate](https://settlens.com/product/ci) and the [GitHub Action guide](https://settlens.com/docs/github-action).

4. **Monitor and share evidence**

   `settlens monitor` compares each passive scan with the previous run, saved in a state file, and can alert a webhook. It alerts once, on the run where a check gets worse or disappears. `settlens evidence attest` signs a report that partners can verify offline. See [Monitor](https://settlens.com/product/monitor).

**settlens.config.json (illustrative values)**

```json
{
  "version": 1,
  "failOn": "high",
  "output": "reports",
  "defaults": {
    "timeoutMs": 8000,
    "repeat": 2,
    "facilitator": "https://facilitator.example.com",
    "expected": {
      "schemes": ["exact"],
      "networks": ["eip155:8453"],
      "payTo": ["0x209693Bc6afc0C5328bA36FaF03C514EF312287C"],
      "assets": ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"],
      "maxAmount": "10000"
    }
  },
  "targets": [
    {
      "name": "forecast",
      "url": "https://api.example.com/v1/forecast"
    },
    {
      "name": "history",
      "url": "https://api.example.com/v1/history"
    }
  ]
}
```

**Run it locally, on staging and on a schedule**

```shell
settlens check --config settlens.config.json

settlens scan https://staging.api.example.com/v1/forecast \
  --facilitator https://facilitator.example.com \
  --active --authorized --authorization-ref CHG-1234

settlens monitor --config settlens.config.json \
  --state settlens.state.json \
  --webhook https://hooks.example.com/settlens
```

> **Values in the example**
>
> `10000` atomic units is 0.01 USDC, which has 6 decimals. The asset is the USDC contract on Base (`eip155:8453`). Replace every value with the terms you actually advertise.

## What the findings look like

Real output from the bundled demo target. One endpoint changes its price between requests, one unlocks for any signature, and one run tightens the ceiling below the advertised price.

**Drift between identical requests**

```text
$ settlens scan http://127.0.0.1:4402/drift \
    --expect-pay-to 0x209693Bc6afc0C5328bA36FaF03C514EF312287C --max-amount 10000 --no-write
Settlens x402 assessment 3b27af01
Targets 1 · Active 0 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/drift
14 pass · 0 fail · 1 warn · 0 skip
  WARN medium   Payment requirements remain stable
       Payment terms changed between identical unauthenticated requests.
 
Total: 14 pass · 0 fail · 1 warn · 0 skip
```

**Payment bypass (authorized probe)**

```text
$ settlens scan http://127.0.0.1:4402/bypass \
    --active --authorized --authorization-ref AUDIT-2026-014 --no-write
Settlens x402 assessment 8701a0bc
Targets 1 · Active 1 · Settlement calls 0
 
127.0.0.1  GET http://127.0.0.1:4402/bypass
13 pass · 1 fail · 1 warn · 0 skip
  FAIL critical Invalid payment authorization is denied
       The endpoint returned HTTP 200 after an invalid payment authorization.
  WARN medium   Invalid payment uses a standard failure status
       Expected HTTP 400 or 402 for invalid payment, received HTTP 200.
 
Total: 13 pass · 1 fail · 1 warn · 0 skip
```

**Monitor: regression since the previous run**

```text
$ settlens monitor --config settlens.config.json --state settlens.state.json
 
demo-secure (GET http://127.0.0.1:4402/secure)
  ALERT [high] policy.maximum_amount: pass -> fail — Amounts stay within the approved ceiling
 
1 blocking alert(s) at or above high.
```

In the monitor run, the configuration's `maxAmount` was lowered from `10000` to `5000` after the first run recorded the baseline, which has the same effect as a price rising above the approved ceiling. The scan exit code is `1` when a failing check meets `--fail-on` and `2` for a CLI or configuration error; a drift warning alone does not fail the default `high` threshold.

## Checks that run on your endpoint

Generated from the Settlens check catalog. Policy checks run only for the fields you configure; active and facilitator checks run only when you enable them.

_Checks relevant to an x402 resource server_

| Check | What Settlens verifies | Severity |
| --- | --- | --- |
| `transport.reachable` ; Endpoint is reachable | Settlens received an HTTP response from the target URL within the timeout (8000 ms by default). Redirects are not followed. | high |
| `transport.status_402` ; Unauthenticated request requires payment | A request without any payment header returns HTTP 402 Payment Required. | high |
| `transport.payment_required_header` ; PAYMENT-REQUIRED header is present | The 402 response carries the PAYMENT-REQUIRED header defined by the x402 V2 HTTP transport. | high |
| `transport.payment_required_encoding` ; PAYMENT-REQUIRED decodes as Base64 JSON | The PAYMENT-REQUIRED value is at most 131072 characters, uses only Base64 or Base64URL characters and decodes to valid JSON nested at most 64 levels deep. | high |
| `schema.official_x402_sdk` ; Official x402 SDK accepts PaymentRequired | The decoded PaymentRequired object passes the parsePaymentRequired schema of the pinned official @x402/core 2.28.0 SDK. | high |
| `schema.payment_required` ; PaymentRequired matches the x402 V2 core shape | x402Version equals 2, resource is an object whose url is an absolute http(s) URL, and accepts is a non-empty array. | high |
| `binding.resource_url` ; Payment requirement is bound to the requested resource | resource.url equals the URL that was requested, including path and query (the fragment is ignored). In evaluate_payment_requirements this check is a warning when no resource URL is supplied, because the binding cannot be confirmed. | high |
| `schema.payment_requirements` ; Accepted payment requirements are well formed | Every accepts entry has a non-empty scheme, a CAIP-2 network, a positive integer atomic amount, a non-empty asset and payTo, and a positive maxTimeoutSeconds. On eip155 networks payTo must be a 20-byte hex address. | high |
| `schema.duplicate_requirements` ; Payment choices are unambiguous | No two accepts entries are identical. | low |
| `freshness.timeout_window` ; Authorization windows are reasonably bounded | Every maxTimeoutSeconds value is at most 3600 seconds (one hour). | medium |
| `policy.scheme` ; Schemes match the local policy | Every advertised scheme is in the policy's allowed schemes (case-insensitive). Evaluated only when a scheme policy is set. | high |
| `policy.network` ; Networks match the local policy | Every advertised CAIP-2 network is in the policy's allowed networks (case-insensitive). Evaluated only when a network policy is set. | high |
| `policy.recipient` ; Recipients match the local policy | Every advertised payTo is in the policy's allowed recipients (EVM hex addresses case-insensitively, other formats such as Solana base58 exactly). Evaluated only when a recipient policy is set. | high |
| `policy.asset` ; Assets match the local policy | Every advertised asset (token contract address or mint) is in the policy's allowed assets (EVM hex addresses case-insensitively, other formats such as Solana mints exactly). Evaluated only when an asset policy is set. | high |
| `policy.maximum_amount` ; Amounts stay within the approved ceiling | Every advertised atomic amount is at most the policy's maxAmount. Amounts are compared in atomic units, so pair maxAmount with an asset policy. Evaluated only when maxAmount is set. | high |
| `stability.payment_requirements` ; Payment requirements remain stable | Repeated identical unauthenticated requests (2 by default) return the same resource, accepts and extensions, ignoring the known ephemeral fields recentBlockhash and lastValidBlockHeight. Skipped when only one sample is available. | medium |
| `transport.https` ; Transport protects payment metadata | The endpoint URL uses HTTPS. Plain HTTP is accepted only for loopback hosts (localhost, 127.0.0.1, ::1) used in local testing. | medium |
| `transport.redirect` ; Payment endpoint does not redirect unexpectedly | Reported as a warning when the endpoint answers with a 3xx redirect. Settlens never follows redirects and records the sanitized Location instead. | medium |
| `transport.response_body_limit` ; Response body stays within the evidence limit | The 402 response body fits within the 1 MiB processing limit. Settlens stops reading at the limit. | medium |
| `security.invalid_signature_fail_closed` ; Invalid payment authorization is denied | With explicit authorization, Settlens sends one intentionally invalid PAYMENT-SIGNATURE (for example an all-zero EVM signature) and expects the resource not to answer with a 2xx status. | critical |
| `transport.invalid_payment_status` ; Invalid payment uses a standard failure status | The response to the intentionally invalid payment is HTTP 400 or 402. | medium |
| `facilitator.requirement_coverage` ; Facilitator covers advertised payment requirements | Every scheme/network pair advertised by the resource appears in the facilitator's /supported kinds for the same x402Version. Skipped when /supported is invalid or unavailable. | high |

The [full check reference](https://settlens.com/docs/checks) also explains why each check matters and how to fix a failure.

## What you get

- **Reports in three formats**

  JSON with a deterministic SHA-256 integrity block, a self-contained HTML report and SARIF. Request header values and response bodies are left out.

  [Report format](https://settlens.com/docs/reports)

- **A CI gate**

  A failing check at or above your threshold fails the build, and the JSON, HTML and SARIF reports of that run record why.

  [CI gate](https://settlens.com/product/ci)

- **Regression alerts**

  Monitor flags a check whose status gets worse since the previous run, for example pass to fail, a new check that fails or warns, and a check that disappears. It can notify a webhook.

  [Monitor](https://settlens.com/product/monitor)

- **Evidence buyers can verify**

  An ed25519 attestation binds the report digest to your signing key and a timestamp; anyone can check it offline.

  [Signed evidence](https://settlens.com/product/evidence)

- **The agent's view of your terms**

  Agents using the Settlens MCP server run the same content checks on your 402 challenge before paying, so a challenge that fails your scan can also hold up their payment.

  [For agent builders](https://settlens.com/use-cases/agent-builders)

- **Guardrails on every run**

  Passive mode never sends PAYMENT-SIGNATURE, and custom headers only go to hosts you allowlist separately from the configuration.

  [Security model](https://settlens.com/docs/security)

## Limits to keep in mind

> **Settlens observes your endpoint from outside**
>
> A passing report means the sampled endpoint behaved consistently with the tested properties during the recorded window. It is evidence of observed behavior, not a certification.

- Passive scans take two samples by default. The active probe sends one class of invalid payload. Neither reviews your application code or exercises every code path.
- Settlens never sends a valid payment and never calls `/settle`, so it does not confirm on-chain settlement or that content is delivered after a real payment.
- Policy checks compare what you advertise with what you configured. Whether the configured recipient is the right one is your review.
- Rate limiting, uptime and pricing strategy are out of scope.
- Run the active probe only on endpoints you own or are authorized to test, preferably on staging with testnet assets.

## Questions from API teams

**Does a scan send a payment to my endpoint?**

No. Passive mode sends ordinary requests without any payment header, and Settlens refuses a user-supplied PAYMENT-SIGNATURE in flags or configuration. Only the authorized active probe sends a signature, and it is intentionally invalid.

**Our endpoint also needs an API key. Can Settlens send it?**

Yes. Add it as a header, for example `--header "Authorization: Bearer ${ASSESSMENT_TOKEN}"`, and allow that exact host with `--allow-host` (or the Action's `allowed-hosts` input). Header values are never written to reports.

**What does the drift warning mean?**

Two identical unauthenticated requests returned a different resource, accepts list or extensions. Known ephemeral Solana fields (recentBlockhash, lastValidBlockHeight) are ignored. If your pricing is intentionally dynamic, confirm it is bound to the requested resource.

**Can I run the active probe against production?**

Only with the endpoint owner's explicit approval, recorded with --authorization-ref. The probe sends one invalid signature and never calls /settle, but we recommend staging and testnet assets first.

**How do I get the CLI?**

Settlens is in a private repository. Pilot participants receive the CLI, which runs on Node.js 20.19 or newer, and the GitHub Action. [Request a pilot](https://settlens.com/contact) to get access.

## Assess one paid endpoint first

Send us the endpoint and the terms you expect it to advertise. The pilot report shows exactly what a paying client sees.

[Request a pilot](https://settlens.com/contact) [See a sample assessment](https://settlens.com/product/assess)

## Sources

1. [x402 protocol specification, version 2 (§5.1 PaymentRequired schema)](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md) — x402 Foundation
2. [x402 V2 HTTP transport: headers and error status codes](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md) — x402 Foundation
3. [CAIP-2: Blockchain ID specification](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) — Chain Agnostic Standards Alliance
