# route402 — complete documentation > Generated from the same markdown that renders https://route402.dev/docs. Every page below is also available individually at https://route402.dev/docs/.md ================================================================================ # Quickstart Source: https://route402.dev/docs/quickstart Summary: Wrap an API, price it, and publish it to the registry in about five minutes. ================================================================================ There are two ways to publish. Both end in the same place: a public endpoint that answers `402 Payment Required` with a machine-readable price, and a listing in the x402 Bazaar registry. ## From an agent This is the shorter path. Add the remote MCP server to your client: ``` https://route402.dev/mcp ``` No install, no config file. The client discovers the authorization server, registers itself, and asks you to approve once. Then describe the API: > List my weather API at api.acme.com on route402.dev, $0.001 a call. The > credential is `sk_live_…` and it goes in an `X-API-Key` header. The agent will call `create_service`, `set_upstream_auth`, `add_endpoint`, `test_endpoint` and `publish_service` in order. See [Publish MCP](/docs/publish-mcp) for the full tool reference. ## From the console 1. **Create the service.** Give it a name, a description, and the base URL of the API you already run. The description is what agents read when choosing between listings, so say what it returns rather than why it is good. 2. **Store the upstream credential.** Choose where it goes — an `Authorization: Bearer` header, a named header, or a query parameter. It is encrypted immediately and is never readable again, including by you. 3. **Add an endpoint.** A path, a method, a price in dollars per call, and a concrete example response. The example is required: an agent uses it to decide whether your response is the shape it needs. 4. **Test it.** This calls your upstream once, exactly as the gateway will, with the stored credential injected. It must pass before you can publish. 5. **Publish.** Every endpoint is registered into the public Bazaar registry. If the registry rejects the listing, you get its reason verbatim. ## What you get ``` https://gw.route402.dev/x/{your-service}/{path} ``` An unauthenticated call to that URL returns `402` with the price. A caller that signs a payment and retries gets your upstream's response. You never issue an API key, and the caller never sees yours. ## What it costs your callers You set the price per endpoint, in dollars. The facilitator resolves the dollar amount to an asset and atomic amount for the network you chose. A caller pays that exact amount; nothing is added on top at call time. ## Before you go live - **Set `payTo`.** It is the address settlement lands at. Publishing is blocked without it. - **Start on Coston2** (`eip155:114`) and move to Flare mainnet (`eip155:14`) once the flow works end to end. - **Check your upstream's terms.** You are responsible for having the right to resell access to it. ================================================================================ # How x402 works Source: https://route402.dev/docs/how-x402-works Summary: The four-leg HTTP exchange that turns 402 Payment Required into a completed call. ================================================================================ HTTP 402 has been in the specification since 1997 and has never meant anything. RFC 9110 §15.5.3 still says it is "reserved for future use". x402 is a convention for what that future use looks like: the server answers with a machine-readable price, and the client pays and retries inside the same conversation. ## The exchange ### 1. The call ```http GET /x/acme-weather/current?city=Zagreb HTTP/1.1 Host: gw.route402.dev Accept: application/json ``` Nothing about this request is special. The caller has no account with the API, no key, and no prior relationship with its owner. ### 2. The price ```http HTTP/1.1 402 Payment Required PAYMENT-REQUIRED: {"x402Version":2,"accepts":[…]} ``` The `accepts` array is the quote. Each entry names a scheme, a CAIP-2 network, an asset, an amount in atomic units, the address to pay, and how long the quote holds: ```json { "scheme": "exact", "network": "eip155:14", "asset": "0xe7cd86e13AC4309349F30B3435a9d337750fC82D", "amount": "1000", "payTo": "0xa2477E16dCB42E2AD80f03FE97D7F1a1646cd1c0", "maxTimeoutSeconds": 60 } ``` A server may offer several entries. A client picks whichever it can pay. ### 3. The signature ```http GET /x/acme-weather/current?city=Zagreb HTTP/1.1 PAYMENT-SIGNATURE: eyJzY2hlbWUiOiJleGFjdCIsIm5ldHdvcmsi… ``` The client signs an authorization for the amount it was quoted and retries the identical request with the signature attached. No wallet pop-up, no redirect, no checkout page — which is exactly why an autonomous agent can complete it. ### 4. The response ```http HTTP/1.1 200 OK Content-Type: application/json {"city":"Zagreb","tempC":21,"sky":"clear"} ``` ## What the gateway does in between Between legs 3 and 4, in order: 1. Send the signature to the facilitator's `/verify`. If it does not verify, answer `402` again. 2. Inject the publisher's upstream credential and call the origin. The buyer never sees that credential; the origin never sees the buyer's payment. 3. Return the origin's response. 4. Ask the facilitator to settle — immediately, or into a batch. **Verify before the origin is called; settle after it succeeds.** The ordering is the whole failure policy: | What happened | What the caller is charged | |---|---| | Upstream returned 5xx or timed out | Nothing. The upstream status is returned as-is. | | Upstream returned 4xx | The call. It was served exactly as requested. | | Verified, but settlement failed later | The call is served; settlement retries. | | The facilitator is unreachable | Nothing — the gateway answers `402`. Failing open would give the API away. | ## v2, not v1 x402 v2 uses CAIP-2 network identifiers and the `PAYMENT-REQUIRED` / `PAYMENT-SIGNATURE` headers. Most tutorials still in circulation describe v1, which used `X-PAYMENT`. If a guide mentions `X-PAYMENT`, it is describing the old protocol. ================================================================================ # Publish MCP server Source: https://route402.dev/docs/publish-mcp Summary: The remote MCP server at /mcp — connection, OAuth, and the six publishing tools. ================================================================================ A remote MCP server that lets an agent publish an API end to end. No `npx`, no local install, no config file editing. ## Connect ``` https://route402.dev/mcp ``` Paste that into any MCP client that supports remote servers. The client will: 1. `POST /mcp` without a token and receive `401` with `WWW-Authenticate: Bearer resource_metadata="…"`. 2. Fetch `/.well-known/oauth-protected-resource` (RFC 9728) to find the authorization server. 3. Fetch `/.well-known/oauth-authorization-server` (RFC 8414) for the endpoints. 4. Register itself at `/register` (RFC 7591) — no manually provisioned `client_id` needed. 5. Send you to a consent screen, then exchange the code with PKCE. Copy-pasteable client config: ```json { "mcpServers": { "route402": { "type": "http", "url": "https://route402.dev/mcp" } } } ``` ### Transport Streamable HTTP. `POST /mcp` carries JSON-RPC and answers with `application/json`; `GET /mcp` with `Accept: text/event-stream` opens the server stream; `DELETE /mcp` ends the session. Protocol versions `2025-06-18`, `2025-03-26` and `2024-11-05` are accepted. ### Scopes | Scope | What it grants | |---|---| | `registry:read` | Browse public listings. The directory is public; nothing needs authorizing to read it. | | `services:write` | Create services, store credentials, and publish under your account. | Tokens are **audience-bound** to `https://route402.dev/mcp`. A token minted for the wallet is rejected here, and vice versa. This server will never issue `wallet:spend` — payment authority lives on a different resource server and is consented separately. Access tokens last one hour. Refresh tokens rotate on every use. ## Tools Every tool returns JSON, never prose. ### `create_service` Registers an upstream API. Requires `name`, `description` (≥ 20 characters) and `upstream_base_url`. Optional: `tags`, `pay_to`, `network`, `icon_url`. Returns `service_id`, `slug`, and the gateway base URL. The upstream must resolve to a public address — private and reserved ranges are refused. ### `set_upstream_auth` Stores the credential the gateway injects when calling your upstream. Requires `service_id`, `placement` (`header`, `query`, or `bearer`) and `value`; `param_name` is required unless placement is `bearer`. **Write-only.** No tool returns this value, the web console cannot display it, and there is no read API. The response carries a keyed `digest` — useful only for answering "did this change?". Call the tool again to rotate. ### `add_endpoint` Defines one path and its price. Requires `service_id`, `method`, `path`, `description` (≥ 10 characters) and `price_usd`. Optional: `input_schema`, `example_input`, `output_schema`, `output_example`. Calling it again for the same method and path updates the definition **and clears its test result** — the contract changed, so the old proof no longer applies. ### `test_endpoint` Calls the upstream once, exactly as the gateway will, with the stored credential injected. Returns `status_code`, `latency_ms`, `content_type`, a `body_preview`, and a `hint` when it fails. Requires `service_id` and `endpoint_id`. `query_params` overrides the endpoint's `example_input` for this call. ### `publish_service` Registers every endpoint into the public Bazaar registry. **Fails unless `test_endpoint` has passed for every endpoint.** A listing that 500s on its first call poisons the directory for everyone. When it cannot publish, it returns `blockers` — a list of `{code, message}` — so the whole set can be fixed in one turn instead of one at a time. When the registry rejects a listing, `rejected_reason` carries its reason verbatim. **Publishing is all-or-nothing.** The service goes live only once every endpoint is in the registry. If the registry rejects a listing or cannot be reached, nothing is published — a service is never live but undiscoverable, and an already-published service is not taken down because the registry hiccuped. `publish_service` is idempotent, so retrying it is the whole recovery procedure. Prices are resolved here, not downstream. `price_usd` is converted to atomic units of the network's settlement asset before it reaches the registry or the gateway, which do no currency resolution of their own. A price with more decimal places than the asset can express is refused rather than rounded, and a network with no configured settlement asset is a blocker — a price nobody can pay is not a listing. ### `get_service_status` Health, listing state, endpoint test results, 30-day analytics, and any outstanding blockers. Every endpoint carries its own `registry_status` (`listed`, `rejected`, `unreachable`, `not_registered`) and `registry_reason`, because the registry accepts or rejects each resource independently. Omit `service_id` to list every service on the account. ## The five-turn flow ``` create_service → svc_8f31 · acme-weather set_upstream_auth → stored, write-only add_endpoint → GET /current · $0.001 test_endpoint → 200 in 198 ms ✓ publish_service → bazaar.status = success ``` ## Errors Tool failures come back as a structured result with `isError: true` and a body of `{ok: false, error, message, …}`. Common codes: | Code | Meaning | |---|---| | `insufficient_scope` | The token lacks `services:write`. | | `upstream_not_allowed` | The URL is malformed, or resolves to a private range. | | `invalid_arguments` | Schema validation failed; the message names the field. | | `not_publishable` | See `blockers` for the full list. | | `temporarily_unavailable` | The platform database is unreachable. Nothing changed. | ================================================================================ # Upstream credentials Source: https://route402.dev/docs/credentials Summary: How your API key is stored, injected, and why nothing can read it back. ================================================================================ route402 holds third-party API keys. That makes the credential vault the most security-sensitive thing here, so the rules are strict and few. ## Write-only, everywhere Once stored, a credential cannot be read back: - No MCP tool returns it. `set_upstream_auth` answers with a keyed digest and nothing else. - The console does not display it, not even masked. There is no "reveal" button because there is no read path to put behind one. - No API endpoint returns it. The single internal endpoint that hands the wrapped blob to the gateway is authenticated with a secret derived from `VAULT_MASTER_KEY`, which only the gateway holds. An agent that can read back a customer's API key is a breach waiting to happen. The safest way to build that is to never build the read path. If you lose the credential, you cannot recover it from here. Get a new one from your upstream and save it again. ## How it is stored Envelope encryption. Each credential gets a fresh 256-bit data key; the data key is itself encrypted under a key derived from `VAULT_MASTER_KEY`. ``` credential --AES-256-GCM--> ciphertext data key --AES-256-GCM--> wrapped data key (under the master key) ``` Rotating the master key re-wraps data keys without touching the ciphertexts. Losing it loses every stored credential — there is no backdoor. ## How it is injected At call time the gateway decrypts in memory, adds the credential to the outbound request, and discards it. It is never logged, never included in an error message, and never returned in a response. Three placements are supported: | Placement | What the upstream receives | |---|---| | `bearer` | `Authorization: Bearer ` | | `header` | `: ` | | `query` | `?=` appended to the URL | ## Egress control A publisher supplies an arbitrary upstream URL and we then make requests to it. Without a guard, "wrap my API" becomes "fetch `http://169.254.169.254/` for me". So every upstream is checked at creation **and again at call time**, because DNS can change in between: - `http://` and `https://` only. - The hostname must resolve, and **every** address it resolves to must be public. A record that returns one public and one private address is still a pivot. - Private, loopback, link-local, CGNAT and multicast ranges are refused, as are `localhost` and `.internal` names. - Credentials embedded in the URL are refused — store them properly instead. - Redirects are not followed, because a redirect can leave the checked host. `EGRESS_BLOCK_PRIVATE_RANGES=false` disables the resolution checks. It exists for local development against a service on your own machine. Do not set it in production. ## Audit Every write, rotation and removal is recorded with the actor that performed it — a console session or a specific MCP client id. ================================================================================ # Directory and trust signals Source: https://route402.dev/docs/directory-and-trust Summary: What the directory reads, how listings are ranked, and which signals are measured rather than claimed. ================================================================================ ## The directory reads the registry, not our database The directory renders `GET /discovery/resources` from the facilitator's public Bazaar registry, plus any registries configured for mirroring. It does not render our own tables. That is deliberate. It means nothing appears in the directory that an agent could not also discover for itself, and it means listings that settle on Base or Solana show up next to listings that settle on Flare. The registry is public infrastructure. Anyone can build a competing directory on the same endpoint, including on ours. What is worth paying for is the layer on top: ranking, reliability history, curation, and search that matches on capability. ## As an API ``` GET /api/directory ``` Returns every listing as JSON — prices, schemas, sources, and trust signals. | Parameter | Effect | |---|---| | `q` | Capability search across names, tags, descriptions, schemas and paths. | | `type` | `http` or `mcp`. | | `maxPrice` | Cheapest accept, in dollars. | | `settled=1` | Only listings whose reliability we measured ourselves. | ## Trust signals An agent about to spend money cannot evaluate a listing the way a developer can. It cannot skim the changelog or ask a colleague. So every listing carries the signals that are expensive to fake: | Signal | What it means | |---|---| | Success rate | Share of paid calls that returned 2xx, over 30 days. | | p95 latency | Measured at our gateway, not self-reported. | | Calls settled | How many paid calls actually completed. | | Settled volume | Money that moved. A spam listing cannot fabricate this. | | First seen | When the listing entered the registry. | ### Not measured is not zero A listing shows **not measured** rather than a number when we have nothing to measure. That happens for services that registered directly with a facilitator and do not route traffic through our gateway — most of the mirrored ones. Uptime is currently shown as unmeasured for every listing. Reporting it honestly needs a synthetic prober that is not running yet, and inferring it from organic traffic would produce a number that looks like uptime without being uptime. Listings are labelled either **settled here** (we serve the traffic and the numbers are ours) or **direct listing** (registered elsewhere, shown exactly as the registry publishes it). ## Ranking In order: 1. Settled volume — money that moved outranks everything. 2. Number of settled calls. 3. Documentation quality — a concrete output example beats an equally unproven listing without one. 4. Name, alphabetically. ## Publish-time quality rules A listing is refused unless it carries a description of at least 20 characters, a per-endpoint description of at least 10, a concrete output example, and a passing `test_endpoint` result. An agent choosing between two endpoints has only this metadata to go on. ================================================================================ # Pricing and settlement Source: https://route402.dev/docs/pricing-and-settlement Summary: How a dollar price becomes an on-chain amount, what settles when, and what is never charged for. ================================================================================ ## You price in dollars Set a price per endpoint in US dollars — `0.001` means a tenth of a cent per call. The facilitator resolves that dollar amount to an asset and an atomic amount for the network you selected, and puts it in the `accepts` array of the 402 response. Pricing in dollars rather than token units means the price does not move when the token does. ## Networks | Network | CAIP-2 | Use it for | |---|---|---| | Coston2 | `eip155:114` | Testing. Start here. | | Flare | `eip155:14` | Production. | Flare is already in the x402 default-asset registry, so a dollar price resolves on it without any special handling on the client side. ## What settles, and when Settlement happens **after** your upstream returns successfully, never before: - **Your upstream returned 5xx or timed out.** Nothing settles. The caller is not charged for your outage, and the upstream status is passed through unchanged. - **Your upstream returned 4xx.** It settles. The call was served exactly as the caller asked, and a bad request is the caller's fault. - **Verification succeeded but settlement failed afterwards.** The response is served and settlement retries. Exposure is tracked per payer and cut off past a limit. - **The facilitator is unreachable.** The gateway answers `402` and serves nothing. Failing open would give your API away for free. ## Where the money goes Straight to the `payTo` address on your service. x402 settles seller ↔ buyer directly; route402 is the index and the gate, not a merchant of record and not a party to the payment. Publishing is blocked until `payTo` is set, because a listing without one quotes a price that cannot be paid. ## Batching Per-call on-chain settlement is uneconomic at a tenth of a cent. The x402 `batch-settlement` scheme addresses this: a buyer deposits once into escrow, each call carries a signed off-chain cumulative voucher — no transaction, no gas, no wait — and one periodic transaction claims many vouchers across many buyers. Gas amortises across every payment in the epoch. The buyer's escrow deposit is also their spend cap, which is what makes autonomous agent spending safe by construction rather than by policy.