# Neuronto Payments: developer reference

Base URL: `https://pay.neuronto.com`. OpenAPI 3.1 at [/openapi.json](https://pay.neuronto.com/openapi.json). Agent guide at [/llms.txt](https://pay.neuronto.com/llms.txt).

## What this is

An [x402](https://x402.org) facilitator. x402 revives HTTP `402 Payment Required`: a server answers an unpaid request with `402` and machine-readable terms, the client signs a stablecoin payment, and a facilitator verifies the signature and settles the transfer on-chain. This origin is that facilitator. It holds no funds: a settlement moves USDC from the payer to the merchant's advertised address in one transaction that the payer signed; the facilitator broadcasts it and pays the gas.

## Endpoints

| Method and path | What it does |
|---|---|
| `GET /health` | Liveness. 200 while accepting traffic. |
| `GET /supported` | Every `(x402Version, scheme, network)` live right now, the signer addresses that pay gas, and per-network facts (asset, EIP-712 name and version). A network is withdrawn here first when it cannot settle. |
| `GET /verify`, `GET /settle` | Self-describing hints naming the expected body. |
| `POST /verify` | Verify a signed payment against its requirements. Moves nothing. Free. |
| `POST /settle` | Settle on-chain. Idempotent per body. Optional `Idempotency-Key` header. |
| `GET /discovery/resources` | Catalogue of resources paid for through this facilitator: terms, method, and input and output shapes where the merchant published them (`bazaar` extension). `limit`, `offset`, `network`. |
| `GET /discovery/stats` | Settlement and catalogue statistics. |
| `GET /status.json` | Observed availability from probe counters (Wilson lower bound, withheld under five probes), network state, the state of address screening, and settlement counts. `/status` is the same thing as a page. |
| `GET /echo` | A live merchant to test a client against: it answers 402, and a payment is refunded in full in the same request. `/echo/status` says whether it is serving. |
| `POST /mcp` | MCP server for agents: `facilitator_status`, `settlement_price`, `find_paid_resource`, `integration_snippet`. No key. GET and DELETE answer 405, never 404. |
| `GET /integrate` | The shortest path from nothing to a paid route, in four frameworks. |
| `GET /pricing` | The price of a settlement, as JSON; `/pricing.md` as Markdown. |
| `GET /.well-known/ard.json` | Agentic Resource Discovery manifest for this API. |

## Accept payments

Point any x402 server SDK at this facilitator and advertise a wallet you control. The same thing
with every framework, and runnable starters, is on [/integrate](https://pay.neuronto.com/integrate). Python (FastAPI):

```python
from x402.http import FacilitatorConfig, HTTPFacilitatorClient, PaymentOption
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
from x402.http.types import RouteConfig
from x402.mechanisms.evm.exact import ExactEvmServerScheme
from x402.server import x402ResourceServer

facilitator = HTTPFacilitatorClient(FacilitatorConfig(url="https://pay.neuronto.com"))
server = x402ResourceServer(facilitator).register("eip155:8453", ExactEvmServerScheme())
routes = {"GET /premium": RouteConfig(
    accepts=[PaymentOption(scheme="exact", price="$0.01", network="eip155:8453", pay_to="0xYourWallet")],
    description="One premium answer")}
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
```

TypeScript (Express): `new HTTPFacilitatorClient({ url: "https://pay.neuronto.com" })` in place of any other facilitator. Nothing else about the SDKs changes. Base (`eip155:8453`) is live. To try it first on Base Sepolia (`eip155:84532`), where settlements are free and USDC comes from a faucet, check that `/supported` lists it: the `networks` object there marks each network `available` or not.

## Pay for things

Any x402 client works unchanged: it never talks to the facilitator, only to the server that answered 402. Testing a client against a live 402 needs a merchant, and this origin runs one:

```bash
curl -i https://pay.neuronto.com/echo          # 402 with terms, then pay it with any x402 client
```

[`/echo`](https://pay.neuronto.com/echo) charges $0.001 on Base and **sends it straight back** in the same request; the gas is ours. It is capped per address and per day, and [`/echo/status`](https://pay.neuronto.com/echo/status) says whether it is serving right now. Nothing about it is special to this facilitator: it is an ordinary x402 resource that happens to refund.

## Request and response shapes

`POST /verify` and `POST /settle` take:

```json
{"x402Version": 2, "paymentPayload": {...}, "paymentRequirements": {...}}
```

`paymentPayload` is the client's signed payload exactly as sent in `PAYMENT-SIGNATURE` (decoded); `paymentRequirements` is the option the server advertised and the client accepted. Verify answers `{"isValid": true, "payer": "0x..."}` or `{"isValid": false, "invalidReason": "...", "invalidMessage": "..."}`. Settle answers `{"success": true, "transaction": "0x...", "network": "eip155:8453", "payer": "0x...", "amount": "10000"}` or `{"success": false, "errorReason": "...", "errorMessage": "...", "transaction": "", "network": "...", "payer": "..."}`. Both keep the x402 shape on every status code, because x402 SDKs parse the body regardless.

Reasons a payer can act on start with `invalid_` (a wrong signature, a used nonce, an expired window, an insufficient balance). Reasons that are the facilitator's (`transaction_failed`, `service_unavailable`) never charge anyone and reach the operator. One more: `sanctioned_address` means the paying, receiving or merchant address appears on the public sanctions list this facilitator screens against; it is refused before any chain call, so nothing is charged and no transaction is created. The list and its age are published on [/status](https://pay.neuronto.com/status).

## Settlement semantics

- **Once per body.** The canonical JSON of the request is the settlement's identity. The identical body is settled once; re-submitting it returns the recorded outcome with `Idempotency-Replayed: true`.
- **`Idempotency-Key`** (optional) binds to the first body it is sent with; the same key with a different body is refused with 422.
- **Pending.** A settlement answers within about 25 seconds or returns `errorReason: "settlement_pending"` with the transaction hash if it was broadcast. It continues in the background. Re-submit the identical body to poll; a pending outcome is never cached, a final one is.
- **Order.** Settlements on one network are processed one at a time, in the order received.
- **Simulation before broadcast.** The transfer is simulated again immediately before it is sent, so a payer that emptied its wallet after verification fails cleanly and costs no gas.
- **Serve after settle.** Deliver the paid resource after `success: true`, not after verify: verification proves the payment can settle, settlement proves it did.

## Errors

`/verify` and `/settle` keep the x402 shape. Everything else, including unknown paths, answers [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem details:

```json
{"type": "https://pay.neuronto.com/developers#not-found", "title": "Not Found", "status": 404, "detail": "No resource is served at GET /no-such-path."}
```

<a id="not-found"></a>**not-found**: no route at that path; there is no `/v1/` prefix. <a id="bad-request"></a>**bad-request**: the body is not JSON; on `/verify` and `/settle` this arrives as `invalid_payload`. <a id="payload-too-large"></a>**payload-too-large**: bodies over 65536 bytes. <a id="too-many-requests"></a>**too-many-requests**: see rate limits. <a id="unprocessable-content"></a>**unprocessable-content**: an `Idempotency-Key` reused with a different body. <a id="internal-server-error"></a>**internal-server-error**: retry with backoff; the response carries no internal detail by design.

## Rate limits

Per client address, three buckets, so a settlement cannot starve the reads inside every paid call: `settle` 50/s (burst 100), `payments-read` ({`/verify`, `/supported`, `/health`}) 30/s (burst 60), `discovery` (everything else) 10/s (burst 20). A refusal is a 429 problem with `Retry-After`; every response names its bucket in `RateLimit-Policy`.

## Networks

| Network | CAIP-2 | v1 name | Asset | Testnet |
|---|---|---|---|---|
| Base Sepolia | `eip155:84532` | `base-sepolia` | USDC `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | yes |
| Base | `eip155:8453` | `base` | USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | no |

The EIP-712 domain a payer signs over differs per network: Base USDC is `USD Coin` version `2`; Base Sepolia USDC is `USDC` version `2`. `/supported` carries both so a server never has to remember.

## Versioning

By the `x402Version` field in the body: `2` uses CAIP-2 networks (`eip155:8453`), `1` uses short names (`base`). Both are served from the same paths. A payment kind is withdrawn by disappearing from `/supported` before the endpoints stop accepting it.
