Skip to content
TraceFast Docs

Paying with x402

Open an entity page in TraceFast →

x402 is an open payment protocol built on the HTTP status code 402 Payment Required. Every TraceFast API method is sold this way too: it answers an unpaid request with a machine-readable price; the client pays in USDC and repeats the request with a proof of payment. No sign-up, no API key, no session. The path is the method's REST path on https://mpp.tracefast.xyz/api/v1, and so are the answer and the refusals.

What you need

  • A wallet holding USDC on Base (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913). No ETH is needed: the payment is an EIP-3009 authorization and the facilitator sponsors the gas.
  • An x402 v2 client. The reference client is @x402/fetch; the mppx CLI also pays x402 challenges. Any client that implements the x402 v2 exact scheme works.

The flow

  1. Request the method. The response is 402 Payment Required with a PAYMENT-REQUIRED header: base64-encoded JSON describing what to pay.

    curl -i https://mpp.tracefast.xyz/api/v1/chains/1/entities/0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97/attributes

    Decoded, the header carries:

    {
      "x402Version": 2,
      "accepts": [{
        "scheme": "exact",
        "network": "eip155:8453",
        "amount": "50000",
        "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "payTo": "0x…",
        "maxTimeoutSeconds": 300,
        "extra": { "name": "USD Coin", "version": "2", "assetTransferMethod": "eip3009" }
      }],
      "resource": { "url": "https://mpp.tracefast.xyz/api/v1/chains/1/entities/0x4838…5f97/attributes" },
      "extensions": {
        "offer-receipt": {
          "info": { "offers": [{ "format": "jws", "acceptIndex": 0, "signature": "eyJhbGciOiJFZERTQSIs…" }] }
        }
      }
    }

    amount is in USDC base units (6 decimals): 50000 = 0.05 USDC. Treat the 402 as the source of truth for the price and the network.

    Each accepts entry comes with a signed offer — a JWS in extensions["offer-receipt"].info.offers[], matched to its entry by acceptIndex. Its payload is what TraceFast commits to before you pay:

    {
      "version": 1,
      "resourceUrl": "https://mpp.tracefast.xyz/api/v1/chains/1/entities/0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97/attributes",
      "scheme": "exact",
      "network": "eip155:8453",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…",
      "amount": "50000",
      "validUntil": 1790958400
    }

    resourceUrl is the full URL you requested, query string included; validUntil is the Unix time the offer stands until. Keep the offer: it is the signed half that the receipt answers. The 402 also names the method's id, unsigned, in extensions["tracefast-receipt"].info.method — the id the receipt will carry.

  2. Pay and retry. The client signs an EIP-3009 transferWithAuthorization for amount to payTo and repeats the same request with the signature in the PAYMENT-SIGNATURE header. With @x402/fetch this is automatic:

    import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
    import { ExactEvmScheme } from "@x402/evm";
    import { privateKeyToAccount } from "viem/accounts";
     
    const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
     
    const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
      schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }], // Base mainnet
    });
     
    const res = await fetchWithPayment(
      "https://mpp.tracefast.xyz/api/v1/chains/1/entities/0x4838b106fce9647bdf1e7877bf73ce8b0bad5f97/attributes"
    );
    console.log(res.status);                          // 200
    console.log(await res.json());                    // { data: {...}, meta: {...} }
    console.log(res.headers.get("PAYMENT-RESPONSE")); // base64 receipt

    Install with npm install @x402/fetch @x402/evm viem.

  3. Read the receipt. A successful call returns 200 with the data and a PAYMENT-RESPONSE header (base64 JSON). Settlement is on-chain and synchronous, so a paid call takes a few seconds. What the header states is described below.

Read the receipt

Decoded, PAYMENT-RESPONSE carries the settlement and two signed documents:

{
  "success": true,
  "payer": "0x…",
  "transaction": "0x…",
  "network": "eip155:8453",
  "extensions": {
    "offer-receipt": { "info": { "receipt": { "format": "jws", "signature": "eyJhbGciOiJFZERTQSIs…" } } },
    "tracefast-receipt": { "info": { "format": "jws", "signature": "eyJhbGciOiJFZERTQSIs…" } }
  }
}

payer is the wallet that paid, transaction the settlement transaction on Base. Both signatures are JWS strings; the payload is the middle, base64url-encoded part.

The x402 receipt

extensions["offer-receipt"].info.receipt is the standard receipt of the x402 offer and receipt extension. It states that this payer paid for this URL.

FieldMeaning
versionVersion of the receipt format, 1
resourceUrlThe full URL that was requested, query string included — the same string the offer signs
payerThe wallet that paid
networkThe network of the payment, eip155:8453
transactionHash of the settlement transaction
issuedAtUnix time, in seconds, the receipt was issued

The TraceFast receipt

extensions["tracefast-receipt"].info is TraceFast's own record of the call: it binds the payment to the answer that was served.

FieldMeaning
versionVersion of the record, 1
requestIdThe id of this call — equal to the answer's X-Request-Id header. Quote it to support
methodThe method's id, as https://mpp.tracefast.xyz/.well-known/x402 lists it
resourceUrlThe full requested URL — the same string as in the offer and in the x402 receipt
payerThe wallet that paid
networkThe network of the payment
assetThe token contract that was paid in
payToThe address that was paid
transactionHash of the settlement transaction
paymentNonceThe nonce of the EIP-3009 authorization you signed — the id of this payment attempt
quotedAmountThe amount the accepted accepts entry asked for, in base units
settledAmountThe amount that was moved, in base units
settledAmountSourceWhere settledAmount was read: authorization — the value of your signed authorization (the exact scheme settles exactly that) — or settle, the settlement result
requirementsSha256SHA-256 of the accepted accepts entry in canonical JSON (JCS, RFC 8785)
statusHTTP status of the answer
contentTypeContent-Type of the answer
bytesLength of the answer's body
bodySha256SHA-256 of the answer's body
issuedAtUnix time, in seconds, the record was issued

bytes and bodySha256 are taken over the body after content decoding — the bytes an HTTP client hands to your code, not the compressed bytes on the wire. Hash the body as you received it, before parsing it.

The offer carries no request id: it is signed before the request runs; requestId exists only in the TraceFast receipt. What joins an offer to the receipts of the call it led to:

  • resourceUrl — one string in all three documents;
  • requirementsSha256 — names the accepts entry you paid, the one the offer points at with acceptIndex;
  • paymentNonce — the nonce of your own signed authorization.

Verify the signatures

The offer and both receipts are signed with EdDSA (Ed25519), with a key that is not the payTo wallet's. The JWS header names the key in kid, a did:web identifier:

{ "alg": "EdDSA", "kid": "did:web:mpp.tracefast.xyz#2026-10" }

Resolve the kid at https://mpp.tracefast.xyz/.well-known/did.json — a free request — and verify against the publicKeyJwk of the entry with that id. Keys rotate and earlier ones stay published, so always resolve the kid of the document in hand; do not pin a key.

The x402 SDK resolves the key itself: verifyOfferSignatureJWS checks the offer, verifyReceiptSignatureJWS either receipt.

import { verifyOfferSignatureJWS, verifyReceiptSignatureJWS } from "@x402/extensions/offer-receipt";
 
await verifyOfferSignatureJWS({ format: "jws", signature: offerJws });
await verifyReceiptSignatureJWS({ format: "jws", signature: receiptJws });
await verifyReceiptSignatureJWS({ format: "jws", signature: tracefastReceiptJws });

Each returns the signed payload and throws if the signature does not verify. All three are plain compact JWS, so any JOSE library verifies them with the same key. A sample to run this against is below.

What is charged

The request runs before the money moves, and the data is released only after the payment settles.

  • An answer with data is charged, once, at the offered amount.
  • An answer whose data is empty is charged too. An empty list is an answer about the chain: nothing matched.
  • An error is not charged. If the method answers with status 400 or above — a malformed address, a block outside the data — you get that status and its JSON error body as Conventions describes, no PAYMENT-RESPONSE header and no receipt. Your signed authorization is never settled.
  • A rejected payment is not charged. See below.

A sample to verify

Two real calls on Base mainnet, made on 2026-10-02 and published as they were received. Nothing is redacted — a changed byte would break a signature or the hash — and the payer is a wallet of TraceFast's own. The amounts inside are facts of these two calls, not a price list.

The same bytes as files, under https://docs.tracefast.xyz/samples/x402/2026-10-02/:

FileWhat it is
served/payment-required.b64The PAYMENT-REQUIRED header of the 402, as received
served/offer.jwsThe signed offer from that header
served/payment-response.b64The PAYMENT-RESPONSE header of the paid answer, as received
served/receipt.jwsThe x402 receipt
served/tracefast-receipt.jwsThe TraceFast receipt
served/body.jsonThe body of the paid answer
refused/payment-required.b64The PAYMENT-REQUIRED header of the refused call's 402
refused/offer.jwsIts signed offer
refused/body.jsonThe error body the paid request got

A paid call that was served

GET https://mpp.tracefast.xyz/api/v1/chains/1/blocks/latest

The offer, from the 402:

eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6bXBwLnRyYWNlZmFzdC54eXojMjAyNi0xMCJ9.eyJhbW91bnQiOiIxMDAwMCIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwibmV0d29yayI6ImVpcDE1NTo4NDUzIiwicGF5VG8iOiIweDJBNjU0OTFFOTNBZDBkNDg0Yjc0NDA1NDAxQTM4ZTJFMEU1RURkN2UiLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vbXBwLnRyYWNlZmFzdC54eXovYXBpL3YxL2NoYWlucy8xL2Jsb2Nrcy9sYXRlc3QiLCJzY2hlbWUiOiJleGFjdCIsInZhbGlkVW50aWwiOjE3OTA5NTg1NjgsInZlcnNpb24iOjF9.jwTRdUHkEFWJP78Y0dnZi2rCJyTPj7dXorFXPE9p-VlHrhPf0Dqw3-2HH0WXswtZwNmFyaRwC_woSS8RcKzMBA

Its payload:

{
  "amount": "10000",
  "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "network": "eip155:8453",
  "payTo": "0x2A65491E93Ad0d484b74405401A38e2E0E5EDd7e",
  "resourceUrl": "https://mpp.tracefast.xyz/api/v1/chains/1/blocks/latest",
  "scheme": "exact",
  "validUntil": 1790958568,
  "version": 1
}

The paid answer: status 200, X-Request-Id: 01a0fd6e-729d-7711-b362-0acbaf50d173, and this body, 1155 bytes:

{"data":{"chainId":1,"number":26105813,"hash":"0x6255d2f14d40747304d0e60fabfa2088d640485c9fa917dfc41b3b0bf4ae398a","parentHash":"0xbcf3fd1cb817d6ab60c3cc5d97e04472e41027384abdd812bf48cf615812cafb","timestamp":1790958263,"feeRecipient":"0xdadb0d80178819f2319190d340ce9a924f783711","stateRoot":"0xaec925a22431289cdb666f9b2c00ae078b5bb108bae0a6ebdc36f815d7f108e2","transactionsRoot":"0xb769d125aa61f479ee56fc78520c50e5739782f63ed5b5eb67f8d87aabf8160a","receiptsRoot":"0x7a83d954104e9f3c48b18e6dd493b71f6495ce79045c9e7f2b445a8322aa0f6a","gasLimit":"60000000","gasUsed":"27110211","baseFeePerGas":"401371541","blobGasUsed":"2097152","excessBlobGas":"197738163","txCount":288,"successfulTxCount":286,"failedTxCount":2,"contractCreationCount":0,"blobTxCount":4,"logCount":718,"totalWithdrawalGwei":"224997063","totalValueWei":"12169290841988197897","burntFees":"10881267165905151","totalGasFees":"26222353767286005","totalTips":"15341086601380854","difficulty":"0","extraData":"0x4275696c6465724e6574","ommersCount":0},"meta":{"chain":"ethereum","timestamp":1790958269180,"cached":false,"asOfBlock":26105813,"docs":"https://docs.tracefast.xyz/api/block-latest"}}

The x402 receipt, from PAYMENT-RESPONSE:

eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6bXBwLnRyYWNlZmFzdC54eXojMjAyNi0xMCJ9.eyJpc3N1ZWRBdCI6MTc5MDk1ODI3MCwibmV0d29yayI6ImVpcDE1NTo4NDUzIiwicGF5ZXIiOiIweDBBZjgzMmMzMzgxOGQ3OTVhN0UzM2ZhMDc3REY1Q2MyNzAyYzU2ZGEiLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vbXBwLnRyYWNlZmFzdC54eXovYXBpL3YxL2NoYWlucy8xL2Jsb2Nrcy9sYXRlc3QiLCJ0cmFuc2FjdGlvbiI6IjB4MGQzMWRkZTk2MWE4OTUyYzhjYTdjYmM3YWJkMzdhNjg4MjhmMGQ5NmExNDZkZGYyZDkyNzE4YzM4MGQxZGViZCIsInZlcnNpb24iOjF9.WlTTjpcBFniITtwj5CJd-p35urKZljV384U3PNOfziJrXFd1no3JzsaFjpSb4KVSAVKyLKHm275KLT4fy840Aw

Its payload:

{
  "issuedAt": 1790958270,
  "network": "eip155:8453",
  "payer": "0x0Af832c33818d795a7E33fa077DF5Cc2702c56da",
  "resourceUrl": "https://mpp.tracefast.xyz/api/v1/chains/1/blocks/latest",
  "transaction": "0x0d31dde961a8952c8ca7cbc7abd37a68828f0d96a146ddf2d92718c380d1debd",
  "version": 1
}

The TraceFast receipt, from the same header:

eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6bXBwLnRyYWNlZmFzdC54eXojMjAyNi0xMCJ9.eyJhc3NldCI6IjB4ODMzNTg5ZkNENmVEYjZFMDhmNGM3QzMyRDRmNzFiNTRiZEEwMjkxMyIsImJvZHlTaGEyNTYiOiIzM2E1ZWQ1OGRlY2Q1NjcxMDI5MWNhMjhkMDMwZDQyNjMwMTI3MDE0NjZhNmJmNzc1MGQ5Y2E4MDNmNTA0MGIyIiwiYnl0ZXMiOjExNTUsImNvbnRlbnRUeXBlIjoiYXBwbGljYXRpb24vanNvbiIsImlzc3VlZEF0IjoxNzkwOTU4MjcwLCJtZXRob2QiOiJibG9jay1sYXRlc3QiLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMiLCJwYXlUbyI6IjB4MkE2NTQ5MUU5M0FkMGQ0ODRiNzQ0MDU0MDFBMzhlMkUwRTVFRGQ3ZSIsInBheWVyIjoiMHgwQWY4MzJjMzM4MThkNzk1YTdFMzNmYTA3N0RGNUNjMjcwMmM1NmRhIiwicGF5bWVudE5vbmNlIjoiMHgwNzNmMTNiYzc5MjU5MTM4NDEyYTA5ZWE0MjhiMzJjYjYyNDljNjQxYjk3YjQ4M2ZiYTcxOWNmNmY1ODAyYWE2IiwicXVvdGVkQW1vdW50IjoiMTAwMDAiLCJyZXF1ZXN0SWQiOiIwMWEwZmQ2ZS03MjlkLTc3MTEtYjM2Mi0wYWNiYWY1MGQxNzMiLCJyZXF1aXJlbWVudHNTaGEyNTYiOiI3MDllM2IyNjg5NTRiNTZlNDRkNzhlMmU1Y2FiNjRjYjZiYTZjNmRiNGQzOTU5M2E0MjA1YjFiNjY5ZDZmNWFlIiwicmVzb3VyY2VVcmwiOiJodHRwczovL21wcC50cmFjZWZhc3QueHl6L2FwaS92MS9jaGFpbnMvMS9ibG9ja3MvbGF0ZXN0Iiwic2V0dGxlZEFtb3VudCI6IjEwMDAwIiwic2V0dGxlZEFtb3VudFNvdXJjZSI6ImF1dGhvcml6YXRpb24iLCJzdGF0dXMiOjIwMCwidHJhbnNhY3Rpb24iOiIweDBkMzFkZGU5NjFhODk1MmM4Y2E3Y2JjN2FiZDM3YTY4ODI4ZjBkOTZhMTQ2ZGRmMmQ5MjcxOGMzODBkMWRlYmQiLCJ2ZXJzaW9uIjoxfQ.krTJQIsNAztU2cgE6tcXU5poF8UgKM6dBFFl8rrciOPDolafaM9gLnY7YocN4E78IpFeuih-_Tqflg0NWgrqAw

Its payload:

{
  "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "bodySha256": "33a5ed58decd56710291ca28d030d4263012701466a6bf7750d9ca803f5040b2",
  "bytes": 1155,
  "contentType": "application/json",
  "issuedAt": 1790958270,
  "method": "block-latest",
  "network": "eip155:8453",
  "payTo": "0x2A65491E93Ad0d484b74405401A38e2E0E5EDd7e",
  "payer": "0x0Af832c33818d795a7E33fa077DF5Cc2702c56da",
  "paymentNonce": "0x073f13bc79259138412a09ea428b32cb6249c641b97b483fba719cf6f5802aa6",
  "quotedAmount": "10000",
  "requestId": "01a0fd6e-729d-7711-b362-0acbaf50d173",
  "requirementsSha256": "709e3b268954b56e44d78e2e5cab64cb6ba6c6db4d39593a4205b1b669d6f5ae",
  "resourceUrl": "https://mpp.tracefast.xyz/api/v1/chains/1/blocks/latest",
  "settledAmount": "10000",
  "settledAmountSource": "authorization",
  "status": 200,
  "transaction": "0x0d31dde961a8952c8ca7cbc7abd37a68828f0d96a146ddf2d92718c380d1debd",
  "version": 1
}

What you can check without trusting this page:

  • All three signatures verify with the key resolved from their kid.

  • resourceUrl is one string in the offer and in both receipts, and it is the URL that was requested.

  • The offer's amount, quotedAmount and settledAmount are equal.

  • requestId equals the answer's X-Request-Id. The offer has no request id.

  • bodySha256 is the SHA-256 of the body, and bytes its length:

    curl -s https://docs.tracefast.xyz/samples/x402/2026-10-02/served/body.json | sha256sum
    # 33a5ed58decd56710291ca28d030d4263012701466a6bf7750d9ca803f5040b2
  • requirementsSha256 is the SHA-256 of the 402's accepts[0] in canonical JSON.

  • transaction is a USDC transfer on Base from payer to payTo, and the USDC contract answers true to authorizationState(payer, paymentNonce): the authorization was used.

import { verifyOfferSignatureJWS, verifyReceiptSignatureJWS } from "@x402/extensions/offer-receipt";
 
const base = "https://docs.tracefast.xyz/samples/x402/2026-10-02/served";
const jws = async (name: string) => (await (await fetch(`${base}/${name}`)).text()).trim();
 
// The key is resolved from each document's kid; none is passed by hand.
await verifyOfferSignatureJWS({ format: "jws", signature: await jws("offer.jws") });
await verifyReceiptSignatureJWS({ format: "jws", signature: await jws("receipt.jws") });
await verifyReceiptSignatureJWS({ format: "jws", signature: await jws("tracefast-receipt.jws") });

A paid call that was refused

GET https://mpp.tracefast.xyz/api/v1/chains/1/transactions/0x00 — 0x00 is not a transaction hash, so the method refuses the request.

The offer, from the 402:

eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6bXBwLnRyYWNlZmFzdC54eXojMjAyNi0xMCJ9.eyJhbW91bnQiOiIxMDAwMCIsImFzc2V0IjoiMHg4MzM1ODlmQ0Q2ZURiNkUwOGY0YzdDMzJENGY3MWI1NGJkQTAyOTEzIiwibmV0d29yayI6ImVpcDE1NTo4NDUzIiwicGF5VG8iOiIweDJBNjU0OTFFOTNBZDBkNDg0Yjc0NDA1NDAxQTM4ZTJFMEU1RURkN2UiLCJyZXNvdXJjZVVybCI6Imh0dHBzOi8vbXBwLnRyYWNlZmFzdC54eXovYXBpL3YxL2NoYWlucy8xL3RyYW5zYWN0aW9ucy8weDAwIiwic2NoZW1lIjoiZXhhY3QiLCJ2YWxpZFVudGlsIjoxNzkwOTU4NTYyLCJ2ZXJzaW9uIjoxfQ.2Vi4Osfw63sV46xPPWIg2zSeLzRe_seiG7t9ccoN18HKUMPWCEqqgi-eAesU-L1o3M_1l3fbZ2XLNGGtGgaOCg

Its payload:

{
  "amount": "10000",
  "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "network": "eip155:8453",
  "payTo": "0x2A65491E93Ad0d484b74405401A38e2E0E5EDd7e",
  "resourceUrl": "https://mpp.tracefast.xyz/api/v1/chains/1/transactions/0x00",
  "scheme": "exact",
  "validUntil": 1790958562,
  "version": 1
}

The client accepted it and repeated the request with a signed authorization, nonce 0x42db76c00cf692f469320fa609e14776793b846a610521fd2eff343d5d759e5c.

The answer: status 400, X-Request-Id: 01a0fd6e-5b81-7272-9134-39ff34ad3137, and this body:

{"error":{"code":"bad_request","details":{"reason":"tx_hash_invalid"},"message":"invalid transaction hash format: expected 0x-prefixed 64 hex chars","requestId":"01a0fd6e-5b81-7272-9134-39ff34ad3137"}}

There is no PAYMENT-RESPONSE header and no receipt. The signed authorization was never settled: the USDC contract answers false to authorizationState(payer, paymentNonce) for that nonce. Nothing was charged.

When payment fails

You get a second 402 instead of a 5xx. Its body carries a detail from the payment facilitator, for example ERC20: transfer amount exceeds balance for an unfunded wallet, or an expired authorization. Nothing is charged for a rejected payment, and verification is free.

Prices and settlement

  • Each method has its own price — today between 0.01 and 0.20 USDC per call, listed in https://mpp.tracefast.xyz/llms.txt. The method's own 402 states its price before anything is charged, and it is authoritative. One payment buys one call, whatever its limit. Over x402 there are no subscriptions, minimums or monthly fees.
  • Payments settle on Base mainnet through the payment facilitator. One on-chain transaction per paid call.
  • TraceFast is listed on Agentic Market, with route templates and prices.

Discovery for agents

Before paying, an agent can read everything it needs for free:

  • https://mpp.tracefast.xyz/llms.txt — methods and prices in plain text;
  • https://mpp.tracefast.xyz/.well-known/x402 — a manifest of every method with its price, as JSON;
  • https://mpp.tracefast.xyz/api/v1/openapi.json — the OpenAPI document;
  • the Agentic Market listing — the x402 Bazaar catalog, where TraceFast is indexed with its route template, parameters and price.

Last updated 22 Oct 2026, 08:06 UTC · verified against TraceFast v1.0.261 · Markdown