POST /v1/verify
Read a settlement transaction and report the payment inside it. Stores nothing.
POST https://api.x402spend.dev/v1/verify
Content-Type: application/json
This is the question underneath POST /v1/reviews, asked without a claim attached. A review says this payer paid this recipient this amount and is kept only if the transaction proves all three. Here there is nothing to prove: you supply a hash, and the server reports what moved.
Try it in the browser — the form is prefilled with a real settlement.
Request
| Field | Type | Required | Notes |
|---|---|---|---|
transaction | string | yes | Hash on EVM, signature on Solana |
network | string | yes | CAIP-2, or a common alias |
Strict: no other field is accepted. Spelling is checked per family before any round trip, so a Solana signature sent with base is 422 rather than a wasted RPC call.
{ "transaction": "0x58156f…", "network": "base-sepolia" }
Response
{
"found": true,
"network": "eip155:84532",
"transaction": "0x58156f…",
"truncated": false,
"transfers": [
{
"asset": "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
"assetLabel": "USDC",
"payer": "0xa2290443ca0a5a599dccc14e4db4df5196e91a59",
"payTo": "0x209693bc6afc0c5328ba36faf03c514ef312287c",
"amount": "10000",
"proof": "payment_traced",
"proofLabel": "payment traced"
}
]
}
| Field | Notes |
|---|---|
asset | Contract address on EVM, mint on Solana. Lowercased on EVM |
assetLabel | USDC when the asset is that chain's Circle-issued USDC. Absent otherwise |
payer | Absent when the chain shows the credit but not its sender |
payTo | The recipient. On Solana this is the account owner, not the token account |
amount | Atomic units |
proof | payment_traced · receipt_only |
proofLabel | The same thing worded for a reader, sent so clients need no copy of the mapping |
truncated | true when the transaction held more than 20 transfers |
assetLabel is only ever set for the chain's own Circle USDC. Any other token is reported by address alone rather than by a name the server would have to guess at.
Found nothing
A transaction that proves no payment is 200 with found: false, not an error — the lookup succeeded and the answer is that there is nothing there.
{ "found": false, "reason": "no ERC-20 Transfer log in this transaction" }
reason uses the same vocabulary as a review rejection: transaction not found, transaction did not succeed on chain, network not supported, transaction has no metadata to verify against, or no ERC-20 Transfer log in this transaction / no SPL token transfer in this transaction.
| Status | When |
|---|---|
200 | The chain answered — with a payment, or with found: false |
422 | Bad shape, or no RPC endpoint configured for that network |
429 | Over 20 per minute |
503 | The chain could not be reached. detail names the host and the failure |
It does not write
This route touches no table. It is a lookup, and one that recorded what it was asked about would be building a log of who checked which transaction. The test suite asserts it by counting rows before and after.
Attribution differs by chain
On EVM every transfer is attributed: the Transfer log names its own sender, so payer is always present and proof is always payment_traced.
On Solana a bare transfer instruction names no authority. When the transaction also carries no balance record for the source account, the transfer is reported with no payer and proof: "receipt_only" — the credit demonstrably happened, and saying so while admitting the sender is unknown is more honest than dropping the transfer or naming a payer the chain did not.