Prove your x402 API charges the right terms
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.
Real output from the bundled demo target, which advertises 0.01 USDC (10000 atomic units) on Base Sepolia.
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
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.
Who this is for
You run an HTTP API, data feed or content endpoint and charge per request with 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 lists what changes. To look at one challenge right away, paste your PAYMENT-REQUIRED header into the free x402 payment request 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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
Authorization windows are too long
maxTimeoutSeconds above 3600 keeps a signed payment usable for longer than settlement needs, which widens replay and misuse exposure.
-
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.
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.
-
Scan the terms you mean to advertise
List the schemes, CAIP-2 networks, recipients (
payTo), tokenassetsand maximum atomic amount in a configuration file.settlens checksends two unauthenticated requests per target by default, follows no redirect and fails a policy check whenever an advertised option falls outside it. -
Prove it fails closed on staging
With your approval reference,
--active --authorized --authorization-refsends one intentionally invalidPAYMENT-SIGNATURE, and the same payload to your facilitator's/verifywhen one is configured. Settlens never calls/settle. -
Gate every pull request
The GitHub composite Action runs the same configuration, fails the job at your
fail-onthreshold (defaulthigh) and writes JSON, HTML and SARIF reports. See the x402 CI gate and the GitHub Action guide. -
Monitor and share evidence
settlens monitorcompares 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 attestsigns a report that partners can verify offline. See Monitor.
{
"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"
}
]
}
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.
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
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
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.
| Check | What Settlens verifies | Severity |
|---|---|---|
transport.reachableEndpoint 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_402Unauthenticated request requires payment | A request without any payment header returns HTTP 402 Payment Required. | high |
transport.payment_required_headerPAYMENT-REQUIRED header is present | The 402 response carries the PAYMENT-REQUIRED header defined by the x402 V2 HTTP transport. | high |
transport.payment_required_encodingPAYMENT-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_sdkOfficial 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_requiredPaymentRequired 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_urlPayment 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_requirementsAccepted 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_requirementsPayment choices are unambiguous | No two accepts entries are identical. | low |
freshness.timeout_windowAuthorization windows are reasonably bounded | Every maxTimeoutSeconds value is at most 3600 seconds (one hour). | medium |
policy.schemeSchemes 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.networkNetworks 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.recipientRecipients 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.assetAssets 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_amountAmounts 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_requirementsPayment 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.httpsTransport 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.redirectPayment 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_limitResponse 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_closedInvalid 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_statusInvalid payment uses a standard failure status | The response to the intentionally invalid payment is HTTP 400 or 402. | medium |
facilitator.requirement_coverageFacilitator 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 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 -
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 -
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 -
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 -
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 -
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
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 to get access.
Sources
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.