Lumière PayCheck Free API key

Payments on Base + Solana

Paid-test receipts

A grade is a summary. A receipt is the evidence behind it.

Every time Lumière PayCheck pays an x402 endpoint to test it, we record a receipt and sign it with our published Ed25519 key. Anyone can check a receipt offline, compare it with the endpoint's live payment terms, and (once that test wallet is retired) find the payment on-chain. Receipts started on November 7, 2026; tests before that have no receipt.

Where to find them

  • On every endpoint page: the Recent paid tests table has a Signed link for each test with a receipt.
  • API (free, no key): GET /v1/test-receipts?url=<endpoint> returns the newest receipts for one endpoint (add &limit= up to 100, or &id= for one receipt).
  • MCP: the paid_test_receipts tool returns the same thing to agents.

What a receipt says

The signed payload:

FieldMeaning
idReceipt id (rcpt_…)
urlThe endpoint we paid
network, assetChain and token, exactly as quoted
amountQuotedThe price in the endpoint's 402 quote (atomic units; USDC has 6 decimals)
amountPaidWhat was actually collected (0 when the endpoint didn't settle the payment)
payToThe payout wallet we paid
termsHashHash of the exact payment terms we paid (below)
responseHash, responseBytesHash and size of the response body we got back
httpStatusThe HTTP status of the paid response
outcomeThe test result: delivered, suspect (first failure, re-test scheduled), bad_response (confirmed failure), payment_failed, auth_required, input_required, example_not_found, mismatch or error (neutral)
testedAtWhen the test ran (UTC)
commitmentA hash that commits to our test wallet and the settlement transaction (below)

Outside the signature, each receipt in the API also says whether the seller later refunded a failed test, and, for retired test wallets, the revealed payer, transaction and salt.

How each hash is made

All hashes are SHA-256, hex.

  • termsHash = sha256 of JSON.stringify(["x402-terms.v1", url, scheme, network, asset, amount, payTo]), with the values exactly as they appeared in the endpoint's 402 quote. Request the endpoint without paying, build the same string from its quote, and compare: the same hash means the terms haven't changed since our test.
  • responseHash = sha256 of the response body we received, as UTF-8 text. Sellers can compare it with their own logs to confirm exactly what they sent us.
  • commitment = sha256 of JSON.stringify(["paycheck-commit.v1", payer, tx, salt]).

Why the payer and transaction come later

If sellers knew which wallet is our tester, they could give it a perfect answer and real buyers something worse, and paid tests would stop meaning anything. So while a test wallet is in use, a receipt only carries a commitment to the payer and the transaction.

When we retire a test wallet, every receipt it signed shows revealed: { payer, tx, salt }. Recompute the commitment from those three values: if it matches the signed commitment, the receipt was about that exact payment, and you can look the transaction up on Basescan or Solscan.

Checking the signature

A receipt is <body>.<signature>, both base64url. body is the base64url-encoded JSON payload. The signature is Ed25519 over the ASCII bytes of paycheck-test-receipt.v1. followed by body. (The prefix means a test receipt can never be mistaken for one of our payment-approval receipts, which use the same key.)

The public key is in our JWKS at /.well-known/jwks.json, kid: "paycheck-receipts-1".

The quick way: POST /v1/test-receipts/verify with {"receipt": "<receipt>"}. Add "payer", "tx" and "salt" to also check a revealed commitment.

In Node.js, with no dependencies:

import { createPublicKey, verify, createHash } from "node:crypto";

const jwks = await (await fetch("https://lumierepaycheck.org/.well-known/jwks.json")).json();
const key = createPublicKey({ key: jwks.keys.find(k => k.kid === "paycheck-receipts-1"), format: "jwk" });

const [body, sig] = receipt.split(".");
const ok = verify(null, Buffer.from("paycheck-test-receipt.v1." + body, "ascii"), key, Buffer.from(sig, "base64url"));
const payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8"));

// Once revealed:
const sha = s => createHash("sha256").update(s).digest("hex");
const matches = sha(JSON.stringify(["paycheck-commit.v1", payer, tx, salt])) === payload.commitment;

What a receipt proves, and what it doesn't

  • It proves what PayCheck recorded about a test, at the time, and that we can't change it afterwards without the signature breaking.
  • With the live 402, anyone can confirm the terms we paid were the endpoint's real terms.
  • With the seller's logs, the seller can confirm the response we graded is the one they sent.
  • After the reveal, anyone can confirm on-chain that the payment happened, from which wallet, for how much.
  • It doesn't publish the response itself. Paid content belongs to the seller, so only its hash is public.