## CAPABILITY: pay-per-call-validate-x402 description: Test any x402 endpoint with a REAL signed payment from a fresh, unfunded, single-use wallet — by construction no funds can move on the target. Returns the facilitator's actual verdict on your endpoint (healthy / broken / payment-ignored / not-x402 / unsupported-network / settled-unfunded-wallet), the decoded 402 challenge, lint findings for silent payment-killers (notably resource descriptions near the ~512-char CDP cap that break every payment while the challenge still looks well-formed), and a 402index discovery report (is your endpoint listed, and is its listing name just its URL — i.e. discoverable?). Config validators check your paperwork; this one hands your endpoint real signed money and reports what the facilitator said back. Built for two callers: sellers wiring a post-deploy check into CI, and agents doing pre-flight due diligence before spending real money on a third-party endpoint. Paid per call via x402 — no account or API key; payment is the authentication. Every successful call charges $0.01 USDC. Call sequence: send the request with no payment; the response is HTTP 402 with payment requirements in the PAYMENT-REQUIRED header; construct a payment proof per the x402 "exact" scheme (EIP-3009 transferWithAuthorization on Base mainnet USDC — Permit2 not supported); retry the identical request with the proof in the Payment-Signature header; the retried request returns the JSON validation report. input: - name: url type: string required: true description: The https x402 endpoint to probe. Must be publicly reachable on Base mainnet (eip155:8453). http, non-443 ports, private/loopback/metadata IPs, and imagcon.app itself are rejected. - name: body type: object required: false description: JSON body the target endpoint expects on its POST (default {}). Used for both the unpaid probe and the signed retry. output: - type: json description: Validation report — verdict, decoded challenge summary, lint findings, facilitator error (verbatim if any), discovery report, and the discarded probe wallet address. auth-required: false scope: financial-transaction ### API (paid) method: POST endpoint: https://imagcon.app/routes/x402/validate body: url: <> body: <> response: json: {verdict, challenge, lint[], facilitator, discovery, probe_wallet} verdicts: healthy - the facilitator engaged the payment and rejected it only because the probe wallet is empty (recognizes "execution reverted", "insufficient funds/balance", etc.). A funded buyer will settle. broken - the paid retry returned a NEW facilitator error that is not an empty-wallet signal (e.g. a schema/invalid_request rejection). Verbatim error in facilitator.error. A funded buyer bounces here too. inconclusive-unfunded - the endpoint re-issued its challenge unchanged and its facilitator gives no reason. An unfunded probe cannot distinguish "correctly rejected an empty wallet" from "ignores payments". Re-run with a funded wallet for a definitive verdict. not-x402 - the unpaid request did not return a decodable 402 challenge. unreachable - network error contacting the target. unsupported-network - the challenge is for a network the probe cannot sign for (v1 signs eip155:8453 only); the decoded challenge is still returned. settled-unfunded-wallet - the target returned 200 to an unfunded wallet; it gives away product without settlement. Red flag. Payment mechanics (read before invoking): this endpoint speaks x402 v2 directly over plain HTTP. An unpaid request returns HTTP 402 with a base64-encoded PAYMENT-REQUIRED header (decodes to {x402Version, accepts[], resource, extensions.bazaar}). The single accepts[] entry specifies scheme "exact", network "eip155:8453" (Base mainnet), asset USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913), amount "10000" (=$0.01, 6 decimals), and a payTo address. Sign an EIP-3009 transferWithAuthorization for that exact amount/payTo using the EIP-712 domain in accepts[0].extra (name/version), retry the identical request with the resulting PaymentPayload base64-encoded in a Payment-Signature header, and the response is HTTP 200 with the JSON report plus a Payment-Response header (base64 JSON: success, payer, transaction, network) confirming on-chain settlement. EIP-3009 is required — Permit2-only clients cannot pay this endpoint. ### PAYLOAD SHAPE (language-agnostic — build exactly this JSON if not using the Python SDK below) The value placed in the Payment-Signature header is the base64 encoding of this exact JSON object. The most common mistake is flattening `scheme`/`network` to the top level — they do NOT belong there. The entire matching entry from `accepts[]` must be copied whole into a top-level `accepted` field. `accepted` is REQUIRED; omitting it produces a payload that fails to parse, and the server's correct response to an unparseable payload is to silently re-issue a fresh 402 — which looks identical to "no payment sent at all" from the outside. ```json { "x402Version": 2, "payload": { "signature": "0x", "authorization": { "from": "0x", "to": "0x1f2A484ef654d49c58c625b09e78B538501D652D", "value": "10000", "validAfter": "0", "validBefore": "", "nonce": "0x" } }, "accepted": { "scheme": "exact", "network": "eip155:8453", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "10000", "payTo": "0x1f2A484ef654d49c58c625b09e78B538501D652D", "maxTimeoutSeconds": 300, "extra": { "name": "USD Coin", "version": "2" } } } ``` `accepted` must be copied verbatim from the `accepts[0]` entry in the 402 response you received — do not reconstruct it by hand from memory. ### EXAMPLE (Python, official x402 SDK) Install: pip install "x402[evm]" requests eth-account ```python from eth_account import Account from x402 import x402ClientSync from x402.mechanisms.evm.signers import EthAccountSigner from x402.mechanisms.evm.exact import ExactEvmClientScheme from x402.http.clients.requests import x402_requests account = Account.from_key("0xYOUR_PRIVATE_KEY") # any wallet with a little USDC on Base client = x402ClientSync() client.register("eip155:8453", ExactEvmClientScheme(EthAccountSigner(account))) session = x402_requests(client) resp = session.post( "https://imagcon.app/routes/x402/validate", json={"url": "https://api.example.com/paid-route", "body": {"prompt": "hi"}}, ) assert resp.status_code == 200 report = resp.json() print(report["verdict"]) # e.g. "healthy" or "broken" for finding in report["lint"]: print(finding["level"], finding["detail"]) ``` Observability note: the SDK session handles the 402 internally — a successful call returns only the final 200 with the report. To observe this validator's own challenge before paying (price, payTo, network), send the request once WITHOUT payment first; the unpaid 402 costs nothing. This is exactly the technique the validator itself uses on your endpoint. ### WALLET PROFILE (optional — paper plan by default) Every successful paid call with wallet profiles enabled returns X-Imagcon-Token and X-Imagcon-Profile-Status. Temporary profiles also return X-Imagcon-Profile-Setup on every paid call so activation is never a one-shot opportunity. X-Imagcon-Api-Key is issued only when a new temporary profile is created and is never stored in plaintext. Header reference: X-Imagcon-Token: tok_... (profile identifier, returned on every paid call) X-Imagcon-Api-Key: ic_live_... (new temporary profile only — unlocks imagcon-mcp tools while the profile remains valid) X-Imagcon-Profile-Setup: https://imagcon.app/routes/x402/profile/activate (every paid call while status=temp) X-Imagcon-Profile-Status: temp or permanent (every paid call) X-Imagcon-Profile-Expires: ISO 8601 expiry when status=temp Each successful paid call resets a temporary profile to 60 days from that payment. If no later payment or activation occurs, the profile expires and its API keys are revoked server-side; a later payment creates a fresh temporary profile. To make permanent: POST /routes/x402/profile/activate with wallet_address, profile_token, name, terms_confirmed, signature, and message. Set rotate_api_key=true to revoke the original key and receive a replacement once if it was lost. Required wallet signature: sign message imagcon.app/profile/activate:{profile_token}:{unix_timestamp} within 5 minutes and include signature + message on activate. Do NOT log or store X-Imagcon-Api-Key or profile_token in chat history. Retry paid calls with X-Imagcon-Token header for wallet-independent continuity when the payer wallet rotates. Send X-Imagcon-Profile: none on a paid request to decline profile creation for that request. To delete an existing profile and its saved wallet data, POST /routes/x402/profile/decline and sign imagcon.app/profile/decline:{profile_token}:{unix_timestamp} within 5 minutes; payment records are retained. If your operator has an Imagcon account, the wallet can be linked to it for free read access to the account's saved images — see link-wallet-to-account.