5wFjTdLyVVrT9SfzonSXxw2EbsGfXvBniSfhhU6auNTP5wFjTd...6auNTPEvery payment is a plain SOL transfer from the buyer's own wallet into the deployment wallet below. The server holds no keys and signs nothing.
The rat report is sold over plain HTTP: your wallet broadcasts a real Solana transfer, this server reads the settlement back from mainnet and unlocks the artifact.
5wFjTdLyVVrT9SfzonSXxw2EbsGfXvBniSfhhU6auNTP5wFjTd...6auNTPEvery payment is a plain SOL transfer from the buyer's own wallet into the deployment wallet below. The server holds no keys and signs nothing.
The price, the payment terms and the receiving wallet all travel inside the 402 challenge itself, so a client can buy without a human ever opening this page.
No facilitator and no custodian: the transaction is re-read from Solana mainnet before anything unlocks, so an unpaid request gets a refusal instead of a report.
A wallet holding at least the threshold of $PromptRat signs one message and reads the same artifact through the holder pass - no transfer, no fee.
Read from /api/x402/rat-report moments ago: the real challenge, the offer, the wallet that receives it, and the field inventory of the artifact. Change the price on the server and this panel follows — none of it is written into the page.
Nothing is for sale right now — the reply carries no offers.
This is the whole protocol contract as a machine reads it: a single base64 object in a single response header. Decode it and you hold the price, the wallet and the artifact schema.
No PAYMENT-REQUIRED header came back — this endpoint is not selling.
Nothing was returned.
Paste any mainnet signature, or let the endpoint hunt down the newest real transfer into the receiving wallet. It runs the same verifier as the paid route and reports what it measured: amount that landed, slot, block time and payer — all read off the chain.
Sample mode walks the receiving wallet's history (getSignaturesForAddress → getTransaction) and judges the newest inbound transfer twice: once at the amount it truly moved, once at this endpoint's own price. The scan is reused for 60 s per server instance.
Hold at least the threshold in your wallet and the same rat report is served without payment. The wallet signs one plain-text challenge message (signing never moves funds); the server verifies that ed25519 signature, consumes the one-off nonce and reads your balance live from the chain. Below the threshold you get the measured numbers back.
GET|POST /api/x402/holder-passFive steps and no middleman. HTTP 402 has been in the spec since 1997 and does nothing by itself — what turns it into a payment rail is a server that can read the chain.
A plain GET returns 402 with the PaymentRequired object base64-encoded in PAYMENT-REQUIRED: the offer, the resource id, the artifact's field inventory and the payload schema.
Your wallet builds and sends a real transaction: 0.005 SOL to the receiving wallet. The server holds no key, signs nothing and never touches the funds.
The same request now carries the base64 PaymentPayload whose payload.signature is your transaction signature. A payer field, if present, is an echo — not a claim the server trusts.
getTransaction re-reads that signature: it must not have failed, must sit inside the accept window, and the pre/post balance tables must show the value landing at the receiving wallet. Transfers routed through a program count the same way.
A first-writer-wins claim burns the signature — one payment, one report — and only then is the artifact assembled and returned together with PAYMENT-RESPONSE.
# 1. no payment yet - read the challenge (the base64 object is in the PAYMENT-REQUIRED header)
curl -i https://promptrat.fun/api/x402/rat-report
# 2. the same request, paid: PAYMENT-SIGNATURE = base64({"x402Version":2,
# "accepted":{...one offer from the challenge...},"payload":{"signature":"<your tx>"}})
curl -i -H "PAYMENT-SIGNATURE: $PAYLOAD" \
"https://promptrat.fun/api/x402/rat-report?mint=GxWqJbWPxMseev7WXYNPLEG8K59v2fAhJ5m5o7Yepump"A 402 from this rail is never a shrug. Each refusal names the reason and carries the numbers measured on chain, so a client can repair its own payment without digging through server logs.
HTTP/1.1 402 Payment Required
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2UiOnsidXJsIjoiaHR0cHM6Ly9wcm9tcHRyYXQuZnVuL2FwaS94NDAy...
{
"error": "underpaid",
"extensions": {
"promptrat-report": {
"info": {
"lastRejection": { "reason": "underpaid", "receivedAtomic": "10490000000", "ageSeconds": 41, "slot": 448820434 }
}
}
}
}The full loop in the two ecosystems that settle Solana payments: Node with @solana/web3.js and Python with solders. Notice what neither needs — no API key, no account, no dashboard.
// npm i @solana/web3.js
import { Connection, PublicKey, SystemProgram, Transaction, LAMPORTS_PER_SOL } from "@solana/web3.js";
const ENDPOINT = "https://promptrat.fun/api/x402/rat-report";
const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");
// 1. ask for the price - no key, no signup, no account
const challengeRes = await fetch(ENDPOINT);
const challenge = JSON.parse(Buffer.from(challengeRes.headers.get("PAYMENT-REQUIRED"), "base64").toString());
const offer = challenge.accepts.find((o) => o.asset === "So11111111111111111111111111111111111111112");
// 2. pay it yourself: your wallet, your signature, the server never signs anything
const tx = new Transaction().add(SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: new PublicKey(offer.payTo),
lamports: Number(offer.amount),
}));
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
const signature = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(signature, "confirmed");
// 3. retry with the settled signature - the server re-reads it from the chain
const payload = Buffer.from(JSON.stringify({
x402Version: 2,
accepted: offer,
payload: { signature },
})).toString("base64");
const paid = await fetch(ENDPOINT, { headers: { "PAYMENT-SIGNATURE": payload } });
const report = await paid.json(); // 200 + PAYMENT-RESPONSE header
console.log(report.payment, report.stats);The whole rail is four environment variables and one store. Price and accept window are read at request time, so both can be retuned without a rebuild.
# .env.local - the wallet that receives the payments PROMPTRAT_X402_PAYTO=<your wallet address> # atomic units = human amount x 10^decimals (1,000,000,000 for SOL) PROMPTRAT_X402_SOL_ATOMIC=5000000 # = 0.005 SOL (9 decimals) # optional: how old a settlement may be and still unlock a report PROMPTRAT_X402_MAX_TIMEOUT_SECONDS=300 # optional: holder-pass threshold (atomic units) - holders at/above it get the report free. PROMPTRAT_X402_HOLDER_ATOMIC=150000000000 # = 150,000 $PromptRat (6 decimals) # the one-report-per-signature store BLOB_READ_WRITE_TOKEN=<vercel blob token>