Payer-report methodology

Anyone can re-run every check on this page against public chain data. We store the signatures and transaction hashes so that our verdicts are reproducible, not trusted.

A payer report is a signed, on-chain-proven statement by someone who actually paid a listed service, saying whether it delivered. It is the one signal in this directory that speaks to outcome — probes prove an endpoint answers correctly; on-chain volume proves people pay it; only a payer can say whether what they bought was any good. Reports are not reviews and not an honor system: every accepted report has passed all of the checks below, and the evidence for each is stored.

The canonical message (v1)

Every report must carry payer_signature: an EIP-191 personal_sign over exactly this message (newline-separated, no trailing newline):

nohumans.directory report v1
listing_id: <id>
tx_hash: <0x...lowercase>
ok: <true|false>

This is a published contract; it will only change with a version bump. You do not need to construct it from documentation: POST a report without a valid signature and the 400 response echoes the exact message_to_sign for your request. The API teaches the format; this page explains why it is shaped this way.

What is checked, and why

A real payment exists. tx_hash must be a successful Base transaction whose logs contain an ERC-20 Transfer, on an asset the listing's live 402 envelope accepts, to the listing's live payTo. We read the listing's payment terms from the endpoint itself at verification time — not from our own cached copy.

The payer is the Transfer log's sender — never the transaction sender. In x402's EIP-3009 flow the transaction is submitted by a facilitator; the wallet that actually paid appears only in the Transfer log. Any gate that read the transaction sender would verify the wrong party.

The report is signed by that payer. Transfer logs are public: anyone can harvest the hash of a payment they never made. The signature requirement means a report is bound to the paying wallet's key — the payment proves a purchase happened; the signature proves the report comes from the purchaser and not a bystander.

The payment is a real purchase, not dust. The transferred amount must be at least 90% of the listing's observed live price. Without this, a fraction of a cent would mint a "distinct payer".

The payment is recent and relevant. The transaction must be no more than 30 days old and must postdate the listing itself.

One report per transaction, one voice per payer. Each transaction hash can be reported exactly once, and a wallet's latest verified report supersedes its earlier ones. N payments are not N votes.

How reports enter ranking

Reports affect a listing's ranking only once 3+ distinct payers have reported on it, and then at 25% weight alongside the 75% probe-based score. Below that floor, reports are displayed but cannot move a listing — a single voice, however loud, is not a signal.

An economic property worth stating: faking a positive report requires actually paying the listing, which enriches the target of the fraud. Negative report-bombing likewise costs the attacker real money — paid to the victim.

Coverage and known gaps

Externally-owned accounts sign reports normally, including EIP-7702 delegated EOAs. ERC-1271 contract wallets (smart accounts) are not yet supportedpersonal_sign recovery cannot verify them. This is an open gap we state rather than hide; support is planned.

First-party listings

Listings operated by the nohumans.directory team are labeled first_party on their pages and are excluded from paid-verification statistics. Probe-based status runs identically for every listing, first-party or not.

What we do not verify

Everything this directory verifies is endpoint behavior — correct 402s, live samples, stable payment destinations, real payments, and signed payer outcomes. We do not verify content accuracy beyond structure, the identity of whoever operates a domain today, or whether a payment-address rotation was legitimate (we detect it and reset trust; we do not judge it). A behavior-based verifier can be passed by a compromised domain that keeps behaving; that is a structural limit, not an oversight, and it is why every signal here says exactly what it measures and no more.

Reference client

Producing the signature from a private key (sign_report.mjs in the repo is the complete version):

import { secp256k1 } from "@noble/curves/secp256k1";
import { keccak_256 } from "@noble/hashes/sha3";

const msg = `nohumans.directory report v1
listing_id: ${listingId}
tx_hash: ${txHash.toLowerCase()}
ok: ${ok}`;
const prefixed = "\x19Ethereum Signed Message:\n" + msg.length + msg;
const digest = keccak_256(new TextEncoder().encode(prefixed));
const sig = secp256k1.sign(digest, privateKey);
// payer_signature = 0x + r + s + v(27/28)

Questions, corrections, disputes: hello@nohumans.directory · API reference · pre-spend check