All posts
x402September 20, 2026·10 min read

How EVIDIQ's x402 Pay-Per-Call MCP Server Actually Works

Picture this: your agent just received a job from a stranger on the open web. The stranger claims to be a "verified data provider." Before your agent hands over USDC, it wants a second opinion — a fast, cheap, automated trust check. The provider's API returns a 402. Your agent panics. Why is it asking for money? What does it expect? How does the signed authorization actually settle?

We've heard this story from teams shipping autonomous agents in 2026. The 402 dance confuses almost every developer the first time. Here's the thing — once you've watched the bytes move once, the protocol is small. This walkthrough pulls apart our x402 pay-per-call MCP server at evidiq.dev/mcp, the same endpoint your agents hit in production.

The Wire Protocol, Byte by Byte

An x402 pay-per-call MCP server is an HTTP service that exposes Model Context Protocol tools behind a payment wall — each call to a paid tool returns an HTTP 402 challenge, the client signs a one-time authorization, and the server settles it atomically. EVIDIQ runs one at evidiq.dev/mcp, where agents fetch the trust skill or pay per verify_agent call in USDT0.

The full sequence is five steps. No hidden servers, no callbacks, no websockets. Just plain HTTP, exactly the way the EVIDIQ docs describe it.

  1. The agent sends a normal JSON-RPC POST to https://evidiq.dev/mcp with tools/call for verify_agent and a target agent identifier.
  2. Our x402 pay-per-call MCP server returns HTTP 402 Payment Required with a JSON body containing an accepts array. No work happens until the price is paid.
  3. The client parses accepts[0], signs an EIP-3009 TransferWithAuthorization message off-chain, and base64-encodes the signature + payload into an X-PAYMENT header.
  4. The agent re-sends the original POST, identical except for the new header. Our server validates the signature, checks the nonce against a short-lived store, and submits the on-chain transfer to the settler.
  5. If settlement succeeds, we execute the real tool (verify_agent), build the trust report, anchor the evidence on 0G Storage, and return the JSON-RPC result along with the receipt headers.

That's it. The retry in step 4 is identical to step 1 — same URL, same body, same headers minus X-PAYMENT. The 402 is the only thing standing between the agent and the answer.

Worth noting: the protocol is stateless from the client's perspective. We don't issue sessions, we don't track cookies, and we don't care if your agent switches IP addresses between requests. The signature binds the payment to a specific amount, recipient, and time window — that's the whole contract.

Reading an accepts[] Object

[IMAGE_PROMPT: A clean modern flat illustration showing a JSON accepts[] object expanding into labeled callouts for scheme, network, amount, asset, payTo, and extra.name/version, drawn in a developer-focused aesthetic with monospace styling, soft teal and amber accent colors, no text other than field labels, no brand logos]

When the 402 lands, your client should parse the body and pick one offer from the accepts array. For our endpoint, the body looks roughly like this:

{
  "x402Version": 2,
  "accepts": [
    {
      "scheme": "exact",
      "network": "x-layer",
      "amount": "100000",
      "asset": "USDT0",
      "payTo": "0xEVIDIQ...settler",
      "extra": {
        "name": "USD₮0",
        "version": "1"
      }
    }
  ]
}

Every field has a job:

  • scheme — currently "exact" for us. Means the amount in the signature must equal amount exactly. No batching, no streaming, no IOU.
  • network — "x-layer" (X Layer, OKX's L2). The chain id must match what your wallet signs against, or the EIP-712 domain separator will be wrong and the transaction reverts.
  • amount — the price in atomic units. For a single verify_agent call we charge 100000, which is 0.10 USDT0 given USDT0's 6 decimals.
  • asset — the token contract address. On X Layer, USDT0 lives at a stable address; if you sign against the wrong contract, the transfer reverts at the settler.
  • payTo — the recipient. We route through our settler contract, not a plain EOA, so disputes and batching are possible downstream.
  • extra.name / extra.version — used by EIP-712 domain construction. Change the version and every existing signature breaks.

Get any one wrong and the retry returns a fresh 402 with a different error field, not a 200. We've watched clients fat-finger amount ("0.1" instead of "100000"), use mainnet instead of x-layer, or copy a stale extra.version from old example code. All three produce the same wall: a polite 402 and zero trust report.

EIP-3009 Without the Cryptography Lecture

You don't need to read the yellowpaper. Here's the practical shape.

EIP-3009 lets a wallet sign a TransferWithAuthorization message off-chain that says: "I authorize exactly amount of asset to move from from (me) to to (payTo), and this authorization is valid only until validBefore, and only usable once via nonce." The signature uses EIP-712 typed data — the same format your wallet already shows when you swap on Uniswap.

What that gives us, the server, is gasless authorization. Your agent doesn't need native X Layer gas tokens. It just needs an EVM private key. The signature is a string the client sends in the header. Settlement — actually moving the USDT0 — happens later, paid by us, with a real on-chain transaction.

Our EVIDIQ Operator docs walk through the exact settled transaction path. Briefly: the signed payload is handed to a settler contract that calls transferWithAuthorization on the USDT0 contract. Because the signature already authorized that specific (from, to, value, validBefore, nonce) tuple, the token contract moves the funds and emits a Transfer event. The settler returns the tx hash, which we attach to the receipt.

Honestly? The mental model is "signed check." Your agent writes a check, we cash it once, and the bank (USDT0 contract) refuses any second attempt.

What Happens on a Bad Signature or Late Nonce

[IMAGE_PROMPT: A modern flat illustration of a stylized diagnostic flowchart showing five failure-path branches from a central HTTP 402 node, each branch ending in a labeled terminal state (expired, reused, wrong network, unknown asset, amount drift), drawn in muted slate with bright red accent states, developer wireframe aesthetic, abstract blockchain nodes, no brand logos or text beyond short labels]

Most integrator bugs aren't clever — they're clock skew, copy-paste errors, or aggressive retries. Here's the failure map we've actually seen when teams wire their client to our pay-per-call MCP server, so you can debug your own client without pinging support.

  1. Expired validBefore — the signer set a window too short (or your local clock is five minutes behind). The on-chain call reverts with FiatTokenV2_2: authorization is expired or equivalent. We surface this as a 402 with error: "invalid_authorization". Fix: regenerate the signature with validBefore = now + 60s.
  2. Reused nonce — your retry logic signed once and replayed the same payload. The token contract reverts with authorization is used. We return a 402 with error: "nonce_consumed". Fix: never cache signed payloads; sign per call.
  3. Wrong domain separator — usually caused by signing on Ethereum mainnet (chainId: 1) and posting against X Layer (chainId: 196). EIP-712 binds the domain to a chain; cross-chain replay is structurally rejected. We return error: "wrong_network". Fix: pin the EIP-712 domain to X Layer.
  4. Wrong asset address — your client signed for USDC on X Layer instead of USDT0. The token contract at the USDT0 address won't recognize the authorization and reverts. Our server logs this as error: "unknown_asset". Fix: read asset from the 402 body and trust the server.
  5. Amount drift — you signed for 0.1 (10^17 wei) but the server expects 100000 (10^5 units because USDT0 has 6 decimals). The signature validates against the typed data you signed, but the caller's value doesn't match the server's amount, so we reject before submitting. Fix: always parse amount as a string and pass it verbatim to the signer.

Each of these returns a 402, not a 500. That's deliberate — per the EVIDIQ Sentinel docs, every payment failure is a payment decision the client gets to retry, not a server crash. We'd rather your agent see "wrong_network" and fix its chain id than watch the request disappear into a void.

If settlement succeeds, the response includes three receipt headers worth saving: X-PAYMENT-RESPONSE (the settled tx hash on X Layer), X-REPORT-HASH (the keccak256 of the canonical trust report), and X-REPORT-SIGNER (the EVIDIQ address that signed the verdict, recoverable by anyone via EIP-191). Those three headers let a downstream agent prove what it paid for, who said the target was trustworthy, and where the evidence is anchored.

Frequently Asked Questions

The open parts are free, forever. The Agent Skill (a single markdown file at evidiq.dev/skill.md), the install helper tools (how_to_install, get_evidiq_skill), and the discovery endpoint at evidiq.dev/x402 all return without a 402. Only verify_agent is paid — it's per call, billed via x402 in USDT0, and the price is in the accepts[] body. If you're building an agent that only needs to install the skill, you'll never see a 402 from us.

Give your agent the trust skill:

curl -s https://evidiq.dev/skill.md
E

EVIDIQ Team

The EVIDIQ team builds the trust layer for the AI agent economy — verifying agent identity and capability, scoring risk, and anchoring every verdict on-chain so agents can decide who to trust before value moves.

More from EVIDIQ Team →
How EVIDIQ's x402 Pay-Per-Call MCP Server Works — EVIDIQ