List your API

Turn any backend API into a pay-per-call service on XDC. Agents discover it in the marketplace and pay in USDC per request — you receive USDC to your address, and never deal with gas or invoicing.

  1. 1

    Make your endpoint x402-payable

    Wrap your routes with x402 payment middleware. It returns 402 with the price until a valid X-PAYMENT header arrives, then verifies and settles the USDC payment through a facilitator and runs your handler.

    import express from "express";
    // x402 payment middleware (e.g. x402-express, or @xdctaskman/x402-server's
    // paymentMiddleware). It returns HTTP 402 until a valid X-PAYMENT arrives,
    // then verifies + settles the USDC payment via a facilitator and continues.
    import { paymentMiddleware } from "@xdctaskman/x402-server";
    
    const app = express();
    app.use(express.json());
    
    app.use(paymentMiddleware({
      payTo,                       // assigned to you at ingestion
      asset: USDC_XDC,             // USDC on XDC (6 decimals)
      network: "xdc",              // mainnet (chainId 50)
      facilitator,                 // verifies + settles; gas is sponsored
      routes: {
        "GET /forecast": { price: "0.01", capability: "weather.forecast" },
      },
    }));
    
    app.get("/forecast", (req, res) => res.json({ tempC: 21 }));
    app.listen(8402);

    Settlement uses EIP-3009, so a relayer pays the XDC gas — your callers (and you) never need native XDC. Don't set payTo yourself; it's assigned to you at ingestion and is stable.

  2. 2

    Describe your service

    Add a provider entry with your endpoints, prices (USDC), and capabilities. This is the curated catalog the marketplace serves.

    {
      "id": "acme-weather",
      "name": "Acme Weather",
      "description": "Weather forecasts over x402.",
      "mcpUrl": "https://acme-weather.example/mcp",
      "services": [
        {
          "url": "https://acme-weather.example/forecast",
          "method": "GET",
          "priceUSDC": "0.01",
          "capability": "weather.forecast"
        }
      ],
      "tags": ["weather", "data"],
      "active": true
    }

    priceUSDC is a decimal string; capability is how agents filter (e.g. weather.forecast). Optionally expose an MCP endpoint (mcpUrl) so agents can discover your resources programmatically.

  3. 3

    Submit to get listed

    Add your service in the admin console (url, method, title, price, payTo, tags). Once saved it appears in the marketplace and is callable by any agent:

    npx xdc-taskman call https://acme-weather.example/forecast

The facilitator (verify + settle)

Your API never touches the chain. The facilitator is the service your middleware calls to verify a signed USDC payment and settle it on-chain — and it pays the XDC gas. You point your middleware at a facilitator URL; that's the whole integration. No wallet, RPC, or gas on your side.

Agent

Signs a USDC payment and sends it in the X-PAYMENT header.

Your API

Returns 402, then calls the facilitator to verify + settle.

Facilitator

Settles on XDC and pays the gas; returns the tx hash.

On a paid request, your middleware makes two server-to-server calls:

  • POST https://xdc-mcp.vercel.app/api/facilitator/verify — is the signed payment valid (signature, balance, amount, not expired)? Nothing moves yet.
  • POST https://xdc-mcp.vercel.app/api/facilitator/settle — broadcast the EIP-3009 transferWithAuthorization; the facilitator pays gas and returns the tx hash.

Facilitator URL for XDC + USDC (this is what partners ask for):

https://xdc-mcp.vercel.app/api/facilitator

Point your middleware at it:

app.use(paymentMiddleware(
  payTo,                                  // your USDC receiving address (XDC)
  { "GET /forecast": { price: "0.01", network: "xdc" } },
  // ↓ the facilitator verifies + settles the payment and pays the gas
  { url: "https://xdc-mcp.vercel.app/api/facilitator" },
));
Heads up: the public XDC facilitator is run by a third party (it pays the gas and may rate-limit). Want gasless settlement sponsored by XDC Taskman with idempotency + per-owner spend caps instead? Get in touch and we'll host a facilitator you can point at.

Error: Facilitator does not support exact on xdc

The facilitator does support it — confirm with:

curl -s https://xdc-mcp.vercel.app/api/facilitator/supported
# → {"kinds":[{"scheme":"exact","network":"xdc","asset":"0xfA2958…8eb1","extra":{"decimals":6}}, …]}

If that returns exact / xdc, the error is in your server config. With @x402/core it almost always means initialize() wasn't awaited before wrapping routes — the supported-kinds list is empty until it fetches /supported:

const server = new x402ResourceServer({
  facilitator: { url: "https://xdc-mcp.vercel.app/api/facilitator" },
});

// ⚠️ REQUIRED before wrapping routes — fetches /supported from the facilitator.
await server.initialize();

createPaidWrappers(server);   // now buildPaymentRequirements sees exact/xdc
startServer();

Also check, in order:

  • 1. await initialize() before createPaidWrappers / building requirements.
  • 2. Facilitator URL is the XDC one (the default Coinbase/x402.org one serves Base, not XDC).
  • 3. Network is exactly "xdc" (not xdc-mainnet, XDC, or 50); asset 0xfA2958…8eb1, 6 decimals.

Full step-by-step + troubleshooting: x402 integration guide (shareable — send it to your devs).

Your 402 response (checklist)

The most common onboarding bug is a non-spec-compliant 402 on a paid route — the client rejects it before paying. Your 402 body must include an accepts array where each entry has every field below. This is exactly what our demo endpoints return.

HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "x402Version": 1,
  "accepts": [
    {
      "scheme": "exact",
      "network": "xdc",
      "maxAmountRequired": "100",
      "resource": "https://your-api.example/v1/assets",
      "description": "Assets List",
      "mimeType": "application/json",
      "payTo": "0xYourReceivingAddress",
      "asset": "0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USDC", "version": "2" }
    }
  ]
}
schemeAlways "exact".
networkExactly "xdc" (mainnet) or "xdc-apothem". Not xdc-mainnet, XDC, or 50.
maxAmountRequiredPrice in atomic units, as a string. USDC has 6 decimals → 0.0001 = "100", 0.01 = "10000", 1 = "1000000".
resourceThe exact URL the client called (canonical, absolute).
payToYour receiving 0x address — ideally the same one your settlement credits and your marketplace listing shows.
assetMainnet USDC 0xfA2958…8eb1 (testnet 0xb5AB…71D4).
maxTimeoutSecondsValidity window, e.g. 60.
extra{ "name": "USDC", "version": "2" } — required so the client can build the EIP-712 domain to sign.

Diff your output against a known-good response:

curl -s https://xdctaskman-x402-server.vercel.app/x402/echo | jq .accepts

Symptom: client errors like maxAmountRequired: Required (received undefined) or resource: Required mean those fields are missing from your accepts entry. Free routes (no 402) won't show this — it's isolated to the paid route's payment config.

FAQ

Do I need a wallet, an RPC, or XDC for gas?
No. Settlement uses EIP-3009 — the facilitator submits the transaction and pays the XDC gas. Neither you nor your callers need native XDC.
What network, asset, and decimals do I use?
Mainnet network xdc (chainId 50), USDC 0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1, 6 decimals. Testnet network xdc-apothem, USDC 0xb5AB…71D4.
What is the facilitator URL?
https://xdc-mcp.vercel.app/api/facilitator — it exposes /supported, /verify, and /settle. Verify support with curl …/api/facilitator/supported.
I get “Facilitator does not support exact on xdc”. Why?
The facilitator does support it — the error is in your server. With @x402/core, await initialize() before wrapping routes, point at the XDC facilitator URL, and use network "xdc". See the red box above and the full guide.
How do I set payTo, and why must it match the marketplace?
payTo is the address USDC settles to — use one you control. Your 402 challenge's payTo is authoritative (that's where money goes), so advertise the same address in any marketplace listing.
Client says “maxAmountRequired: Required” / “resource: Required”.
Your 402's accepts entry is missing required fields. Each entry needs scheme, network ("xdc"), maxAmountRequired (atomic units string), resource (the exact URL), payTo, asset, maxTimeoutSeconds, and extra. See the 402 response checklist above.
My route never returns 402 (it's always free).
The payment middleware isn't in front of that route, or the route key doesn't match. Register the exact METHOD /path in the middleware config and ensure it runs before your handler.
How do agents actually pay my API?
They sign an EIP-3009 USDC authorization and send it in the X-PAYMENT header on retry. See the caller side.

You get

  • • USDC per call, settled on XDC
  • • No gas to manage (sponsored via EIP-3009)
  • • Agent-ready discovery (marketplace + MCP)

Reference

  • @xdctaskman/x402-server — Express + middleware + MCP
  • • Wire format: x402 exact-evm, USDC (EIP-3009)
  • Agent side — how callers pay