Verifying x402 Settlements and Catching Misdirected Payments with Agent Commerce Gate
How to bind an x402 settlement receipt to an Agent Commerce Gate decision on ContextIQ, verify it against USDC transfers on Base independently, and feed the noPriorRugReports check that flags endpoints that took payment but sent funds elsewhere.
A clean x402 payment challenge tells you a counterparty is well-formed enough to ask for money correctly. It tells you nothing about what happens after you pay it. Agent Commerce Gate scores the challenge and the counterparty's ERC-8004 identity before your agent signs anything — but the moment the actual USDC transfer happens is a second, separate event, on-chain, that the pre-payment check has no visibility into by itself. Closing that gap is what the receipt-verification endpoint and the noPriorRugReports check are for. This post is a hands-on walkthrough of using both today, plus what's still manual about it.
The Failure Mode This Catches
A payment challenge can be perfectly well-formed — correct payTo, correct asset, correct maxAmountRequired — and the actual settlement can still go somewhere else entirely. Nothing about the x402 spec forces a seller's backend to honor its own advertised challenge once a signed authorization comes in. The only way to know whether a given endpoint actually pays out consistently with what it asks for is to check the chain after money has actually moved, and to remember the outcome the next time someone is about to pay that same endpoint.
That's a two-part problem: verifying one settlement independently, and turning a history of settlements into a signal future callers can see before they pay. Agent Commerce Gate does both.
Step 1: Run the Check and Keep the checkId
Every POST /api/v1/policy-check (or /api/v1/policy-check/x402-v2) response includes a checkId — a reference to the exact decision, reasoning, and expected payment terms that were computed at that moment:
curl -X POST https://contextiq.trango-compute.com/api/v1/policy-check \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"endpoint": "https://api.example.com/paid-resource", "agentId": "1", "network": "ethereum"}'
{
"decision": "allow",
"score": 88,
"maxScore": 100,
"checkId": "b3f1c2a4-...-a1b2c3d4e5f6"
}
Hold onto checkId. It's what ties whatever happens next back to this specific decision, including the exact payTo address and amount the endpoint declared at check time.
Step 2: Actually Pay the Endpoint
This step happens entirely outside ContextIQ — your agent signs and submits the x402 payment to the counterparty the way it normally would. The earlier technical walkthrough covers signing an x402 payment with x402-fetch if you need that part spelled out. Whatever your payment flow is, the only thing you need out of it is the resulting transaction hash.
Step 3: Bind the Receipt
Once you have a transaction hash, submit it against the checkId from step 1:
curl -X POST https://contextiq.trango-compute.com/api/v1/policy-check/receipt \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"checkId": "b3f1c2a4-...-a1b2c3d4e5f6", "transaction": "0x1234...abcd"}'
{
"checkId": "b3f1c2a4-...-a1b2c3d4e5f6",
"decision": { "decision": "allow", "score": 88, "maxScore": 100 },
"receipt": {
"network": "base",
"status": "confirmed",
"blockNumber": "18234521",
"payer": "0x857b...b66",
"transfers": [{ "token": "0x8335...a02913", "from": "0x857b...b66", "to": "0x2096...287C", "valueAtomic": "20000" }],
"matchesExpectedPayment": true,
"evidence": ["Transaction mined in block 18234521 with status success", "Transfer to 0x2096...287C for 20000 matches the expected payment"],
"warnings": [],
"verifiedAt": "2026-09-11T12:00:00.000Z"
}
}
This isn't a trust-me response. verifyX402Receipt independently fetches the transaction receipt via eth_getTransactionReceipt, decodes the ERC-20 Transfer event out of the logs, and compares the actual to address and amount against what the original decision's payment challenge declared — never against anything the caller asserts. matchesExpectedPayment: true means the money genuinely went where it was supposed to.
What Counts as a Rug Signal — and What Deliberately Doesn't
Only one specific outcome gets recorded as a negative signal against the endpoint: status === "confirmed" and matchesExpectedPayment === false — the transaction mined successfully, but the transfer went to a different address, or for less than the declared amount, than the payment challenge promised.
Two adjacent outcomes are explicitly not treated as rug signals, on purpose:
| Status | What it actually means | Counted as a rug signal? |
|---|---|---|
confirmed, matchesExpectedPayment: false | Money moved, not to the declared recipient/amount | Yes |
reverted | Transaction failed on-chain — payer lost gas, nothing settled | No |
not_found | Transaction hasn't been mined yet (or doesn't exist) | No |
A reverted transaction means nothing was paid at all — counting it would penalize an endpoint for someone else's failed transaction, not the endpoint's own behavior. Only a transaction that succeeded but paid the wrong party is evidence the endpoint itself did something wrong.
Where This Shows Up in the Score
Every check contributes a named line to the reasons[] array. The one this feeds is no_prior_rug_reports, worth 10 of the default rubric's 100 points:
{
"check": "no_prior_rug_reports",
"points": 0,
"maxPoints": 10,
"detail": "1 prior payment to this endpoint confirmed on-chain but did NOT settle to the declared payTo/amount"
}
A nonzero rug count doesn't just cost 10 points — it caps the overall decision at warn, regardless of how well everything else scores. A perfect identity, deep reputation, and a flawless payment challenge can't outvote a documented history of misdirected settlement. That mirrors how Agent Commerce Gate already treats a malformed challenge or an unregistered identity: some findings are strong enough that no amount of unrelated good behavior should quietly override them.
The Catch: Receipts Only Count if You Own Both Calls
Receipt verification is always returned to whoever calls it — anyone can check whether a transaction settled correctly. But it's only persisted as a shared signal (visible to other callers checking the same endpoint later) when the account submitting the receipt is the same account that ran the original check. If you paid anonymously via a per-call x402 payment for the original decision, or you're verifying a receipt against someone else's checkId, you still get a real, independently-verified answer back — it just doesn't feed the shared rug-report count. Otherwise anyone could spend $0.02, submit an unrelated confirmed transaction hash against a competitor's checkId, and manufacture a false rug report against them.
Practically, that means building up this signal today requires calling both /api/v1/policy-check and /api/v1/policy-check/receipt with a Pro API key, under the same account, for every endpoint you actually pay.
Try It
Both endpoints are live at $0.02 USDC per call, same as the rest of Agent Commerce Gate — via a Pro API key or a per-call x402 payment. Full request/response shapes for every endpoint, including this one, are on the Developers page, no account required to read them.
Follow Trango Compute on LinkedIn
We post updates on new tools, context engineering patterns, and LLM cost research.