# dry.0tx.io > Checker for x402 endpoints on Solana. Send an endpoint URL; dry requests it the way a buyer would, stops before paying, and reports what would prevent a real buyer from completing the payment. One HTTP request, structured JSON, no setup, no account, no key, free. **Why dry?** A seller sees their endpoint return a 402 and assumes it works. Nothing tests whether a stranger can actually pay until a real buyer fails. The common Solana failures are invisible from the seller's side: a payout wallet with no token account for the asset, a fee payer that changes between serverless instances, a network and a token mint that don't match. dry finds them in one call and tells you how to fix each one. **dry never pays, signs, or holds a private key.** v0 sends unpaid requests and reads public chain state. A `no_problems_found` verdict means every check passed or warned; it is not proof that a payment would succeed. ## Check an endpoint POST https://dry.0tx.io/check Content-Type: application/json {"url": "https://api.example.com/weather", "method": "GET", "body": null} - `url` is required and must be `https://`. - `method` is `GET` (default) or `POST`. - `body` is optional JSON sent with a `POST` (16 KB max). Send what a buyer would send, e.g. for drip: `{"url": "https://drip.0tx.io/v1/sol", "method": "POST", "body": {"devnet_address": ""}}`. dry sends five unpaid requests: three at once (to land on more than one serverless instance), then two in sequence. It never forwards your headers to the target, follows at most three redirects, times out after 5 seconds per request and reads at most 64 KB. ## Response {"spec": "dry/0", "id": "chk_…", "url": "…", "method": "GET", "checked_at": "", "verdict": "problems_found", "x402_version": 2, "responses": [{"status": 402}, {"status": 402}, {"status": 402}, {"status": 402}, {"status": 402}], "endpoint_checks": [{"name": "returns_402", "status": "pass", "detail": "…"}, {"name": "has_solana_option", "status": "pass"}], "options": [{"network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "network_seen": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "asset": "EPjF…", "pay_to": "…", "amount": "10000", "fee_payer": "…", "checks": [{"name": "payto_can_receive", "status": "fail", "detail": "No token account exists for this wallet and token on Solana mainnet; …", "fix": "Create the associated token account for the payout wallet before accepting payments: …"}, …]}]} `verdict`: - `no_problems_found`: every check passed or warned. Not "payable": dry has not paid. - `problems_found`: at least one check failed. Every fail has a `detail` and a `fix` you can act on. - `not_testable`: no 402 came back, or no payment option on Solana mainnet or devnet was offered. Not a judgement on the endpoint. Check `status` is `pass`, `fail`, `warn` or `skipped`. A check that cannot be decided (RPC down, non-USDC token) is `skipped` with the reason, never guessed into a `fail`. Every `fail` and `warn` carries `detail` and `fix`. `x402_version` is 2 when the requirements came in the `PAYMENT-REQUIRED` header and 1 when they came in the JSON body. `network` is the CAIP-2 id dry evaluated against; `network_seen` is what the endpoint sent (V1 names like `solana` are mapped). ## Checks Endpoint-level: | check | pass when | fail when | |---|---|---| | returns_402 | all five responses are 402 with requirements that parse | a mix of 402 and other statuses (listed), or requirements that cannot be read. No 402 at all gives verdict `not_testable` | | has_solana_option | at least one option is on Solana mainnet or devnet | none is (verdict `not_testable`, "not a Solana endpoint") | Per Solana payment option: | check | pass when | fail when | skipped when | |---|---|---|---| | network_matches_token | asset is the USDC mint for the stated network and exists there | a USDC mint on the wrong network; a mint that does not exist on the network; an asset not owned by the Token or Token-2022 program | any other token ("non-USDC token, not evaluated") | | payto_can_receive | the payout wallet's associated token account for the asset exists on the network (derived with the token program that owns the mint, exactly as x402 clients do) | it does not exist, or payTo is itself a token account | the asset could not be resolved | | requirements_stable | fee payer, payout wallet, asset and amount are identical across all five responses | any of them differs (the field and the values seen are named) | fewer than two readable responses | | fee_payer_funded | the fee payer account exists and holds at least 0.05 SOL | no `extra.feePayer`, or the account does not exist on the network; below 0.05 SOL is a `warn` | RPC read failed | Known mints: USDC mainnet `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`, USDC devnet `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU`. Networks: mainnet `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`, devnet `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` (V1 `solana` and `solana-devnet` accepted). Testnet is not checked. ## Use it after every deploy 1. Deploy your x402 endpoint. 2. `POST https://dry.0tx.io/check` with its URL (and the method and body a buyer would use). 3. If `verdict` is `problems_found`, apply each `fix` and check again. 4. Stop at `no_problems_found`. Then make one real paid call before telling anyone it works; dry does not pay. ## Errors | HTTP | error | Meaning | |---|---|---| | 400 | invalid_url | `url` missing or not an absolute URL | | 400 | url_refused | not `https://`, has credentials, or points at a private, loopback, link-local, metadata or local address (also enforced at DNS resolution and on every redirect) | | 400 | host_opted_out | the host's owner asked dry not to check it | | 400 | invalid_method | `method` is not `GET` or `POST` | | 400 | invalid_body | `body` sent with `GET`, or larger than 16 KB | | 404 | not_found | no such endpoint; see Allowed endpoints | | 429 | rate_limited | more than 6 checks per minute from your IP (or the global limit). Wait a minute | | 500 | internal_error | dry's fault; try again | ## Allowed endpoints Everything is on `https://dry.0tx.io`. These are the only endpoints: - `GET /`: the website - `GET /llms.txt`: this file - `GET /SKILL.md`: these instructions as an agent skill - `GET /openapi.json`: machine-readable spec - `POST /check`: check an endpoint ## Storage and removal Every check is stored: URL, host, time, verdict and the full result. Per-endpoint results are never published; only aggregates are. To have a URL's results deleted, or to stop dry from checking a host, message @0sumTX on X (https://x.com/0sumTX) with the hostname. ## Crawler dry's outbound requests carry the user agent `dry.0tx.io/0 (x402 endpoint checker; +https://dry.0tx.io/#crawler)`: five unpaid requests per check, no payment header, no retries, 5-second timeout, 64 KB read cap. Opt out by messaging @0sumTX with the hostname. ## Out of scope in v0 Making a real payment, chains other than Solana, accounts and API keys, a public list of endpoints or their results, scheduled re-checks. ## Who runs this dry is built and run by @0sumTX (https://x.com/0sumTX), a member of ABCDao (https://x.com/ablockchaindao), alongside drip.0tx.io, the x402 devnet faucet for agents.