Best Practices for Registering an x402 Service
When an agent calls a paid endpoint and gets HTTP 402, the next step depends on the response body. The caller needs enough information to choose a payment option, sign the request, retry, or decide that the service is not ready to use.
The common failure mode is simple: the price is in docs, the token address is missing, or the demo path is buried on a landing page. Put the payment contract, request shape, demo path, and fallback path in the response the agent already has.
This checklist comes from paid ERC-8004 services indexed by The Spawn. The reference capture below is Social Intel API on April 15, 2026: a production Instagram influencer search API that accepts USDC on Base and Solana, exposes a free demo mode, and returns a 402 body that both software and a human builder can act on.
A usable x402 service tells the caller what resource costs money, which chains and assets it accepts, how to try a demo, how to shape the request, and how to retry after payment.
In brief: An x402 payment response is the machine-readable 402 body that carries
resource,accepts[], amount, asset, network, recipient, and timeout. A wallet-free demo path lets agents and humans inspect response shape before paying, while ERC-8004 metadata should declare paid services only when the live endpoint returns a parseable 402 challenge.
What a caller sees
When an unpaid request hits the reference service, it returns HTTP 402 with JSON. The minimum useful shape is small:
{
"x402Version": 2,
"resource": {
"url": "https://api.example.dev/v1/search",
"description": "Paid search result",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "500000",
"payTo": "0x0000000000000000000000000000000000000000",
"maxTimeoutSeconds": 300
}
],
"demo": {
"available": true,
"example": "https://api.example.dev/v1/search?demo=true"
}
}
The production Social Intel reference had more fields because it exposed multiple networks, getting-started commands, client hints, and a contact fallback. This example is trimmed for readability:
{
"x402Version": 2,
"error": "Payment required",
"resource": {
"url": "https://socialintel.dev/v1/search",
"description": "Search Instagram influencers by keyword, category, country...",
"mimeType": "application/json"
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "500000",
"payTo": "0xB1Acd9E0269023546074400A434e703B646AaBBa",
"maxTimeoutSeconds": 300,
"extra": { "name": "USD Coin", "version": "2" }
},
{
"scheme": "exact",
"network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "500000",
"payTo": "Fj2MVB43hefNzf2XtJp9wGYqS6zHyLzewZ2Vbo4EmBvV",
"maxTimeoutSeconds": 300
}
],
"demo": {
"available": true,
"description": "Try free: add ?demo=true for 3 results without payment.",
"example": "https://socialintel.dev/v1/search?demo=true",
"curl": "curl 'https://socialintel.dev/v1/search?demo=true'"
},
"getting_started": {
"free_demo": "https://socialintel.dev/v1/search?demo=true",
"one_command_paid": "npx agentcash@latest fetch \"https://socialintel.dev/v1/search\"",
"price": "$0.50 USDC for this request",
"chains": ["Solana", "Base (EVM)"],
"x402_clients": [
"AgentCash: npx agentcash@latest fetch \"...\"",
"Python: pip install \"x402[httpx,evm]\"",
"Node.js: npm install x402"
]
},
"contact": {
"description": "If payment is unavailable, tell us what you need.",
"endpoint": "POST https://socialintel.dev/v1/contact",
"body": {
"message": "(required) Describe what data you need",
"contact": "(optional) Email, webhook URL, or wallet address"
}
}
}
The useful part is not the length of the JSON. It is the absence of guessing. A client can read accepts, select a supported network, sign a payment, and retry. A builder can run the demo curl without opening the website. If payment is blocked, the same response exposes a contact endpoint.
That is the bar for a paid agent service: the 402 response should carry the next action.
What the 402 response needs
1. Payment options with real chain details
Each item in accepts should say exactly how to pay: scheme, network, token asset, base-unit amount, destination payTo, and timeout.
Use CAIP-2 style network identifiers where your x402 client expects them, such as eip155:8453 for Base. For current EVM USDC payment flows checked by The Spawn, include the EIP-712 domain fields in extra; the payment flow needs token name and version to build the signature.
The amount value belongs in base units, so USDC with 6 decimals represents $0.50 as "500000" rather than "0.50". Human-readable price labels belong in getting_started, away from the field a wallet signs.
Services that support more than one network should put each network in accepts. A single-network service can still work; it just needs to be explicit about the chain the caller must hold funds on.
2. A demo path that needs no wallet
A demo endpoint lets a caller inspect response shape before paying. It should use real data, be clearly marked as a preview, and work with a bare curl.
Useful demo rules:
- Return a small sample from the same code path as the paid endpoint.
- Include a marker such as
"demo": trueor an upgrade message in the result. - Require no auth header, wallet connection, or API key.
- Rate-limit it so the preview is useful without replacing the paid product.
The exact quota depends on your service cost. A data API can often show a few rows; a costly compute service may need a cached fixture. In both cases, the preview has to verify the output contract before the paid call.
3. Input and output schemas
A payment response tells the caller how to pay. A schema tells it what request is worth paying for.
If the endpoint accepts query parameters or a JSON body, expose the same shape you would publish in OpenAPI or MCP tools/list. The schema should name parameters, types, defaults, bounds, and required fields.
"outputSchema": {
"input": {
"type": "http",
"method": "GET",
"schema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Keyword search" },
"country": { "type": "string", "description": "ISO code or full name" },
"limit": { "type": "integer", "default": 20, "minimum": 1, "maximum": 100 }
}
}
},
"output": {
"type": "json",
"schema": {
"type": "object",
"properties": {
"results": { "type": "array" },
"count": { "type": "integer" }
},
"required": ["results", "count"]
}
}
}
Avoid maintaining a separate hidden schema for agents. Reuse the operation schema from your API, MCP tool, or OpenAPI document so the paid path and discovery path stay in sync.
4. Getting-started instructions
Autonomous clients may ignore this block, but builders use it. Include the demo URL, a paid invocation, the human-readable price, supported chains, and client libraries you have tested.
Use executable instructions instead of general advice, because a line like npx agentcash@latest fetch "https://..." is more useful than "use an x402-compatible client." Python or Node package references should include exact install commands.
5. A contact fallback
Some callers cannot pay immediately because their wallet is empty, their chain is unsupported, or they need a higher-volume plan before wiring payment.
A contact endpoint in the 402 body gives those callers somewhere to go without leaving the API flow. Ask for the need, a reply channel, and any relevant wallet or webhook. Account creation should not be required just to ask a question.
Register more than the raw API
Paid APIs are easier to discover when the same backend is exposed through the protocols agent clients already inspect.
| Surface | Example endpoint | Role |
|---|---|---|
| API | /v1/search | Serves direct HTTP requests, returns 402 for paid calls, and accepts ?demo=true for preview. |
| MCP | /mcp | tools/list exposes the callable tool and input schema; tools/call executes it. |
| A2A | /.well-known/agent-card.json | Publishes a machine-readable agent card with skills and service URL. |
| Web | / | Human page with docs, price, examples, and demo link. |
This does not require four separate products. The API, MCP tool, A2A handler, and web page can all call the same service function.
For a Python MCP server, the shape can stay simple:
@mcp.tool()
def search_leads(query: str = None, country: str = None, limit: int = 20, demo: bool = False):
"""Search Instagram influencers by demographics, location, or keyword."""
# same logic as your REST endpoint
The decorator only handles plumbing. What matters is parity: tools/list and the REST docs describe the same arguments, and paid calls return the same x402 payment requirements no matter which surface reaches them. For the broader protocol split, see MCP vs A2A.
How this affects Spawn scoring
The Spawn scorer currently combines metadata, live endpoint behavior, and community feedback. A paid service does well when it is both declared and callable.
| Scoring area | Why the 402 setup matters |
|---|---|
| Metadata | A real description, image, services list, registrations, and x402Support make the agent understandable before any live probe runs. |
| Liveness and protocol checks | Reachable HTTP, valid JSON, MCP discovery, A2A discovery, and a parseable x402 response show that the service can be used today. |
| Payment readiness | A 402 response with accepts[], token details, and a demo path gives The Spawn evidence that payment is a working flow rather than a metadata label. |
| Community | On-chain feedback is separate. Good payment plumbing does not replace usage, but it removes a common blocker before users can leave feedback. |
Score gains are a side effect of a usable path: discover the service, inspect the tool, try a demo, pay, retry, and receive the result.
The scoring-specific pieces are metadata, service declarations, live endpoint behavior, and payment-readiness evidence. The x402 protocol defines the payment exchange; The Spawn decides how much of that exchange is enough evidence for an agent card.
Mistakes to remove before registering
Localhost in production responses
Search the full 402 body for localhost, 127.0.0.1, private hostnames, and staging domains. The reference service once exposed paid_url: "http://localhost:8000/v1/search" in a demo response field. Any caller that follows that URL fails outside the developer machine.
Human-readable payment amounts
The accepts array should not contain "amount": "0.50". Wallet code expects the atomic amount. For 0.50 USDC, use "500000" and put $0.50 USDC in the human-facing instruction block.
Payment details only in docs
Documentation is useful after discovery, but the 402 response itself still needs accepts[], asset, amount, network, payTo, and timeout. An agent should not need to scrape your docs to know how to pay.
Demo mode that does not exercise the real shape
A static marketing response is not enough. The demo should prove the fields, error shape, and result envelope the paid request will use. If live data is expensive, return a cached example from the same schema and label it clearly.
MCP without payment parity
If the REST path returns a good 402 but the MCP path returns an opaque error, agent clients will see the service as broken. Make tools/call expose or route to the same payment requirement used by the API.
A practical registration checklist
- Unpaid requests return HTTP 402 with
x402Version,resource, andaccepts[]. - Token amounts use base units, and asset addresses are real production addresses.
- EVM payment options include
extra.nameandextra.versionwhen EIP-712 signing needs them. - The 402 body shows a wallet-free demo URL.
- Input and output schemas come from the same source used by the API or MCP tool.
- The response includes a one-command paid example and a human-readable price.
- Callers that cannot pay yet have a contact endpoint.
- API, MCP, A2A, and web surfaces point to the same backend when those protocols fit the service.
- ERC-8004 metadata uses real service URLs and declares
x402Support. - A clean shell can call the public URL before you mint or refresh metadata.
The goal is not a large metadata file. The goal is a paid service an agent can use without a private explanation from the builder.
Check your x402 setup: thespawn.io/check
FAQ
Schema in the 402 body
It should include enough schema for a client to know what it is paying for. That can be inline JSON Schema, an OpenAPI link, an MCP tools/list schema, or a compact operation description. Avoid hiding the request shape only on a marketing page.
Demo mode
The protocol can work without demo mode, but discovery works better with it. A demo lets agents and builders verify response shape without payment, which reduces accidental paid calls and makes the service easier to trust.
ERC-8004 metadata relationship
ERC-8004 metadata helps indexers discover the paid service. The 402 response proves the paid path is real. Use both: metadata for discovery, x402 for payment terms, and the registration checklist for the full agent card. For the basic payment flow, read x402 for APIs and Agents; for local installation after discovery, read Install an ERC-8004 Agent in Your Editor.
Public references. Use the live Social Intel agent card, The Spawn checker, Best Practices for Registering Onchain AI Agents, and the current x402 docs when validating payment behavior. x402 behavior and header names can change; verify against current public protocol docs before shipping production payment code.