---
title: "The x402 endpoint preflight (v1)"
description: "Send a URL; we GET it once and report whether it answers a well-formed x402 v2 payment challenge: a 402 status, a parseable base64 PAYMENT-REQUIRED header, accepts entries a client can actually sign against, and structurally valid signed offers if declared. Free, no account. One probe, one moment — a shape check, never an uptime claim."
canonical: "https://scvd.store/api/preflight/v1"
url: "https://scvd.store/api/preflight/v1"
battery: "v1"
method: "POST"
price: "free"
auth: "none"
defect_vocabulary: "https://scvd.store/defects"
---

# The x402 endpoint preflight — v1

Send a URL; we GET it once and report whether it answers a well-formed x402 v2 payment challenge: a 402 status, a parseable base64 PAYMENT-REQUIRED header, accepts entries a client can actually sign against, and structurally valid signed offers if declared. Free, no account. One probe, one moment — a shape check, never an uptime claim.

## How to call it

```
POST https://scvd.store/api/preflight/v1
Content-Type: application/json

{"url": "https://your-endpoint/..."}
```

- **`url`** — REQUIRED. The https URL a buyer would GET expecting your 402 — your buy endpoint, not your homepage.

Free, and no account exists to open. The whole procedure for every door
in this store is at https://scvd.store/auth.md.

## What it checks

- The endpoint answers 402 Payment Required (a 200 is the 'listed but functionally absent' failure; a redirect is refused, because payment clients refuse it too).
- PAYMENT-REQUIRED header present and base64-JSON parseable — x402 v2 clients read the challenge there, not from the body.
- x402Version is 2.
- Every accepts entry carries scheme, network, amount, asset, payTo as strings — the same fields this store's own till refuses to sign offers without.
- extensions.bazaar, if declared, carries a parseable info block (what discovery ingestion actually reads).
- extensions['offer-receipt'] signed offers, if present, are structurally valid JWS. Their signatures are NOT verified here — that needs a second request to the issuer's did:web, which this probe refuses to make in your name. The conformance desk does it free.

## What it cannot check

- Delivery. This unpaid probe cannot observe a purchase. The launch_check rung records one bounded paid attempt and what was delivered; even that is not a guarantee about future purchases.
- Reliability. One probe is one moment; this is not a monitor and its output is not an uptime claim.
- Verify/settle-time failures — wallet state, signatures over a specific payment, on-chain conditions. Those belong to the payment attempt, not the endpoint's shape.

## Common failures this catches

- **`stuck_repeating_402`** — A client that keeps getting 402 after attaching PAYMENT-SIGNATURE is very often paying against the wrong network: accepts offering eip155:84532 (Base Sepolia) or another testnet while the buyer is on Base mainnet, eip155:8453. The probe flags known testnets as an advisory.
- **`listed_but_functionally_absent`** — A directory lists your URL as an x402 endpoint but it answers 200, 404 or 500 instead of a 402. Independent probing found the majority of one directory's listings in this state. This is check one.
- **`unparseable_challenge`** — PAYMENT-REQUIRED header missing or not base64 JSON — surfaces client-side as 'Invalid payment header format' or a silent parse failure.
- **`amount_units`** — Amounts are ATOMIC units (USDC: 6 decimals, $0.005 = "5000"). A decimal point in an accepts amount usually means dollar-typed pricing, off by a factor of a million; the probe flags it.
- **`unpayable_payto`** — payTo must be the bytes a payment signs over — a 20-byte 0x address on EVM rails, a base58 pubkey on Solana. A name (ENS, Basename, SNS, Unstoppable) is a resolution step the protocol does not define, so most clients throw inside their signing library and you never learn a buyer came; the probe names the registry and the chain it resolves on. It also catches the wallet pasted into the wrong rail's entry — a 0x address in a solana accepts entry or base58 in an eip155 one — which nobody can pay, resolver or not.
- **`inputs_only_discovered_by_paying`** — If your resource needs parameters and the challenge does not declare them (extensions.bazaar.info.input), a buyer finds out by being refused AFTER signing a payment — and their ledger records that as YOUR endpoint failing. In the August 2026 field run this was the largest single cause of refused purchases at otherwise-working endpoints. The probe flags the missing contract and credits a declared one.
- **`after_verify_failures`** — Facilitator codes like invalid_exact_evm_payload_signature or settle_exact_failed_onchain happen AFTER the challenge stage, at verify/settle time, and depend on the specific payment attempt — a preflight cannot catch them and this one does not pretend to. For the artifact half (do the signed offers verify against the issuer's published key), use POST https://scvd.store/api/conformance/v1.

## Rate limits

Two ceilings, both ours: a strict per-isolate bucket and a global best-effort cap of 60 probes/minute across all callers. Past either you get a 429 that says the budget is our cost bound, not a fact about your endpoint. The global cap is eventually-consistent, so it can run slightly generous — never tighter than stated. This endpoint makes one outbound GET per call to a host you chose; the cap is what keeps it a checker rather than a relay.

## What a verdict means

HTTP 200 and a report naming every check with ok true or false, the advisories, the verdict under this battery, and `also_under` carrying the other battery's verdict on the same probe. A not_ready verdict is a successful call — the tool worked and found something. Only the codes below mean the call itself did not happen.

The named defect vocabulary every verdict cites: https://scvd.store/defects.
The battery manifest, with the stable check ids and a recomputable
ruleset digest: https://scvd.store/api/preflight/checks.

## Several doors at once

https://scvd.store/api/preflight/batch takes up to ten URLs in one call. Each one
is a real probe and is metered as one — batching saves you connections,
not outbound requests.
