The Wire Protocol, Byte by Byte
An x402 pay-per-call MCP server is an HTTP endpoint exposing MCP tools where each privileged call is gated by a machine-readable payment challenge. EVIDIQ implements this on x402 v2, paid in USDT0 on X Layer and settled by an EIP-3009 transferWithAuthorization the client signs off-chain. No API keys, no subscriptions, no gas from your side — just a signed message.
Picture the moment. Your agent needs to know whether the counterparty it's about to transact with is real, declared its capabilities honestly, and has any reputation at all. It calls verify_agent. It hits a wall: HTTP 402 Payment Required. That wall is exactly what we wanted to build — a wall that costs nothing to read and a small amount of USDT0 to pass through.
Here's the sequence our x402 pay-per-call MCP server runs through on every paid tool call:
- The MCP client sends
POST https://evidiq.dev/mcpwith a JSON-RPC body requestingtools/callforverify_agent. - Our edge handler returns 402 with an
accepts[]array describing how the bill is paid. - The client reads
accepts[0], seesscheme: "exact"and anassetof USDT0 on X Layer, and constructs an EIP-3009transferWithAuthorizationpayload. - The client signs that payload with the agent's EVM key. No gas, no nonce management, no
eth_sendRawTransaction. - The client retries the same
POSTwith the signature packed into anX-PAYMENTheader. - Our server verifies the signature, hands the authorization to our settler, which submits it on-chain and credits our settlement address in USDT0.
- The MCP tool runs, the report is returned, and the response is sealed with a signed trust verdict.
That's the happy path. One round-trip in the lucky case, plus a verify-and-forward step on our side. The interesting work happens in step 6 — the settler is what actually turns your signed message into a settled payment on X Layer. For the server-side view of this same flow, see the EVIDIQ Sentinel docs; for the broader surface area, EVIDIQ docs is the root.
Worth noting: how_to_install and get_evidiq_skill don't follow this path. Those two tools are free — they return install instructions and the raw skill text without ever emitting a 402. Only verify_agent is pay-per-call. That's a deliberate split: install friction is on us, evidence friction is on the caller.
Reading an accepts[] Object
[IMAGE_PROMPT: A clean developer-focused flat illustration showing a JSON object labeled "accepts[]" floating in the center, with thin callout lines pointing to each labeled field — scheme, network, amount, asset, payTo, extra.name, extra.version. Background is a soft dark slate with subtle grid lines, and small USDT0 token glyphs scattered around as decoration. No text other than field labels. Modern technical editorial style, no real brand logos.]
The 402 your client receives is not an error in the human sense — it's a fully specified bill. Here's what every field in accepts[] actually does, and what blows up if you read one wrong:
- scheme — Must be
"exact". Anything else (like"upto") means a price ceiling, and we don't support that mode. A client that treats"upto"as"exact"will sign an authorization that overpays on a future round and silently lose funds. - network — The CAIP-2 chain id. For us, that's
eip155:196(X Layer). If your client hardcodes Base mainnet (eip155:8453), the EIP-712 domain separator hashes wrong and the settler rejects the signature — but only after you've already burned the round-trip. - amount — Integer string in the smallest unit. USDT0 has 6 decimals, so
100000is ten cents. Off-by-decimal bugs are the single most common integration mistake. - asset — The contract address of USDT0 on X Layer. Don't substitute the Ethereum-mainnet USDT address. The chain id and asset must agree; we cross-check both.
- payTo — Our settlement address. Your authorization must name this exactly; otherwise the settler will dutifully move USDT0 to the wrong account and you will not get your report.
- extra.name and extra.version — These bind the EIP-712 domain separator. We use
name: "USDT0"andversion: "1". If your signer constructs the domain with a stale version, the recovered address won't match your signer, and you'll see a 402 with areasonfield of"invalid_signature".
The shape mirrors x402 v2, so any compliant client library should parse it without glue code. If you're rolling your own, the EVIDIQ site links to a reference client in the open-source EVIDIQ skill repo that you can read top to bottom.
Honest opinion: more than half of "x402 doesn't work" tickets we've seen are decimal mistakes. Read amount as a string, parse it as a BigInt, and never — never — divide by 10^18 by reflex.
EIP-3009 Without the Cryptography Lecture
Here's the thing: EIP-3009 is the single decision that makes this whole product shape possible. Without it, an x402 pay-per-call MCP server would either need you to hold ETH for gas or to pre-deposit into a custodial balance — and both of those ruin the value proposition.
EIP-3009 adds two functions to a token contract: transferWithAuthorization and receiveWithAuthorization. The flow:
- The payer signs a structured EIP-712 message that names the recipient, the amount, a
validAfterwindow, avalidBeforedeadline, and a random 32-bytenonce. - The signature authorises a one-time transfer of that exact amount from the payer to the recipient. The signature itself is not a transaction — it's just data.
- Anyone can later submit that authorization to the token contract by calling
transferWithAuthorization. The contract verifies the signature, checks the nonce hasn't been used, checks the deadline hasn't passed, and atomically moves the tokens. Whoever submits the transaction pays the gas.
That last clause is the unlock. The payer signs. We submit. You never need a wei of X Layer ETH in your agent wallet to use our MCP. Your agent can run on a fresh keypair with zero native balance, sign one message, and get a paid-for trust report back.
Our settler — the small worker that lives between the MCP server and X Layer — is what does the submitting. It holds a tiny hot wallet used only for gas and only for calling transferWithAuthorization on USDT0. We never hold your funds, never custody your tokens, and never get approval to move anything from your wallet: the EIP-3009 authorization is a one-shot, capped, time-bounded cheque. You can read exactly what it permits by replaying the EIP-712 hash in any wallet that supports EIP-712.
The EVIDIQ Operator docs walk through the settler's deployment and its gas-funding model if you're integrating into a system that bills back the gas. The headline point is simpler than the cryptography: you sign, we deliver. That's it.
What Happens on a Bad Signature or Late Nonce
[IMAGE_PROMPT: A flat developer-style diagram showing four diverging arrow paths from a central "signing step" box. Each arrow leads to a labeled terminal box: "Expired", "Nonce Reused", "Wrong Domain", "Chain Mismatch". Each terminal box has a small x402-style status indicator and a short descriptive caption. Background is a muted off-white with subtle dot grid. No real brand logos, abstract blockchain nodes in muted blue and gray. Editorial technical style.]
Failure paths matter more than happy paths, because in production your agent will hit them. We've structured our 402 responses to make every common failure self-diagnosing. Here's the catalogue:
validBeforealready passed. Your client built the authorization with a short deadline, the network lagged, or your clock is skewed. Our verifier returns 402 withreason: "expired"and echoes thevalidBeforevalue so you can compare against your own wall clock.noncealready used. EIP-3009 nonces are single-use. If your signer reused a nonce (or copied one across sessions), the on-chain call reverts. Our server surfaces that asreason: "nonce_used". Generate a fresh 32-byte random nonce per call.- Wrong domain separator (
name/versionmismatch). The signer recovered an address that doesn't matchfrom. We returnreason: "invalid_signature"and include the domain we expected in the response body. If you see this, you've drifted from ourextra.name/extra.versionvalues — re-check. - Chain id in domain doesn't match
network. Same as above in spirit — your signer thinks it's on a different chain. We returnreason: "wrong_chain"with the expected CAIP-2 id. - Insufficient USDT0 balance. Your wallet doesn't have the funds at execution time. We return
reason: "insufficient_balance"before submitting on-chain, so you don't pay for a revert.
The honest bit: the x402 spec is still maturing, and we treat these reasons as part of our public contract. If you build a client against our MCP and a new failure mode shows up, you'll see it labelled rather than dumped as error: "internal". We made that choice early, and it has saved every debugging session we've had with integrators since.
If you're chasing a 402 that none of the above match, the EVIDIQ Notary docs cover the report-sealing layer that runs after the tool call — a separate but adjacent system that handles the cryptographic notarisation of the trust verdict itself.