/discovery/resources to find paid APIs and MCP tools they can use.
Listing is automatic: there is no registration form, account, or manual submission. The facilitator indexes your resource from the payments it processes — if they carry your discovery declaration.
How listing works
Three parties cooperate to get a resource listed:- Your server declares. Your 402 response includes a bazaar discovery declaration describing the endpoint (method, input/output shape, service metadata).
- The buyer’s client echoes. The client copies your declaration from the 402 response into the payment payload it sends to the facilitator. This is required client behavior in x402 v2 — the client must include at least the extension info it received.
- The facilitator catalogs. On
/verifyand/settle, the facilitator extracts the declaration from the payment payload and upserts your catalog entry asynchronously. - The catalog worker admits the resource. Before a resource is listed for the first time, the worker checks the URL against the host policy below and sends one read-only probe (
HEAD, thenGETon404/405; the probe never sendsPOST,PUT,PATCHorDELETE). A resource is listed when either proof holds: the indexing payment was a settlement (a buyer only pays after receiving your402), or the read-only probe itself answers402 Payment Required. A verify-only first listing of aPOST/PUT/PATCH/DELETEor MCP resource (or one with no declared method) is deferred withresource_needs_settlementuntil its first settled payment; aGETresource answering something other than402unpaid is refused asresource_not_x402. Unreachable hosts,GETpaths answering404/405/410, and hosts resolving to private address space are refused regardless. Existing entries are refreshed without a probe.
Step 2 is where most “my resource never appears” reports come from. If the buyer’s client drops the
extensions object when building its payment payload, the facilitator never sees your declaration — no matter how many payments settle successfully. Use the EXTENSION-RESPONSES header to tell the cases apart./verify as well as /settle. Verification moves no funds, so you can list or refresh a resource without a settled payment.
Declaring your resource
x402 v2 servers declare via thebazaar extension on the 402 response. The @x402/extensions package builds a valid declaration for you (declareDiscoveryExtension), including the JSON schema the facilitator validates against. The declaration lives in the 402 body’s top-level extensions object:
serviceName, tags, and iconUrl fields on the resource object are service-level metadata the Bazaar uses to present your listing; they are persisted along with description and mimeType.
x402 v1 servers declare through outputSchema.input on the payment requirements themselves (with type and method required). Because v1 discovery info rides inside the payment requirements — which your server controls end to end — v1 listing does not depend on the buyer’s client echoing anything.
MCP servers declare per tool. The catalog key is (resource, toolName), so a server exposing several tools at one URL gets one entry per tool.
Host policy
The resource URL must be an absolutehttp or https URL on a public host. Declarations that fail these rules are rejected on the payment response itself (see rejection reasons) and never enter the catalog:
Reachability is checked asynchronously by the catalog worker, never on the payment path. Its verdict is visible through
/discovery/listing-status: resource_unreachable (DNS failure, connection error, timeout, or a 5xx), resource_not_found (404/405/410 on a GET/HEAD resource), resource_not_x402 (a GET/HEAD resource answered unpaid without a 402), and resource_needs_settlement (a POST/PUT/PATCH/DELETE, MCP, or method-less resource seen only through /verify; it lists on its first settlement). All probes are read-only, so a listing can never cause a request to reach your application handler.
Reading EXTENSION-RESPONSES
Every/verify and /settle response from the facilitator reports what happened to your declaration via the EXTENSION-RESPONSES header — a base64-encoded JSON object keyed by extension:
A rejected or missing declaration never affects the payment itself: verification and settlement succeed or fail on their own terms.
Rejection reasons
What gets catalogued
Each entry in/discovery/resources carries:
Refresh semantics
- Entries are upserted from every payment that carries the extension —
accepts, schemas, metadata, andlastUpdatedall refresh. Identical repeats of an unchanged listing are collapsed so a busy endpoint does not rewrite its row on every payment; a payment whose declaration,accepts, or service metadata changed is written straight away. - Refresh is forward-only. Correcting your declaration does not rewrite the catalog until the next extension-carrying payment arrives.
- Listed resources are probed on an ongoing basis with read-only
HEAD/GETrequests (every listed row at least once a day; recently settling resources more often). A resource that fails three consecutive probes spanning at least 48 hours — connection failures, timeouts,5xx, or404/405/410on aGET/HEADresource — is hidden from/discovery/resourcesand/discovery/search; the first healthy probe, or a successful settlement through the facilitator (identical settlements are coalesced, so allow up to five minutes), relists it automatically. A404/405from aPOST/PUT/PATCH/DELETE, MCP, or method-less resource is inconclusive and changes nothing. A hostname that starts resolving to private address space is hidden immediately and is relisted only by a healthy probe. Hidden rows keep their data and are visible through/discovery/listing-status. - There is no re-index endpoint. To force a refresh, make one verify-shaped payment against your own endpoint through a client that echoes extensions —
/verifycatalogs and moves no funds. Confirm with theprocessingheader status.
Endpoints
GET /discovery/resources
These are the filters the x402 Bazaar specification defines for
ListDiscoveryResourcesParams, so bazaar.listResources({ type: "mcp" }) from @x402/extensions works unchanged. The payTo, scheme, and network filters must all be satisfied by a single accepts entry: a resource that takes exact on Base and upto on Solana is not returned for scheme=exact&network=solana:…, because it does not accept that pair.
Returns { items, pagination: { limit, offset, total }, x402Version }, newest first. The unfiltered listing is cached for about a minute; filtered requests are served from indexes and not cached. The legacy /list path redirects here permanently.
GET /discovery/search
Natural-language search over the visible catalog, as defined by the Bazaar specification (bazaar.search() in @x402/extensions targets it).
Returns
{ resources, partialResults, pagination: null, x402Version }. resources uses the same item shape as the list endpoint, ranked by relevance then recency. partialResults is true when more matches exist than were returned. If no whole-word match exists, a prefix match is tried before returning an empty list. Responses are never cached.
GET /discovery/listing-status
Answers “is my resource listed, and if not, why” without a support thread.
Returns
{ resource, toolName, listed, hidden, hiddenReason, lastUpdated, lastProbe: { at, status, httpStatus } | null, lastWrite: { status, reason, detail, at, source } | null, policy }. lastWrite is the catalog worker’s most recent outcome for this resource (kept for 30 days): listed, updated, or rejected with the reason. policy is the static host-policy verdict for the URL you passed. Responds 404 (RFC 9457 problem) when the resource has neither a catalog row nor a recent write outcome.
GET /discovery/stats
Aggregate catalog and settlement statistics. Note the three catalog counts:catalogEntries but one resource and at most one new host.
Opting out and delisting
- v1: declare
discoverable: falseinsideoutputSchema.inputand the facilitator will not index the resource. - v2: simplest is to omit the
bazaarextension from your 402 — with nothing to echo, nothing is indexed. - Dead resources leave on their own. Take the endpoint down (connection refused, DNS gone, or
404/410on aGETresource) and the probes above hide it within about two days; it comes back automatically if it reappears. Entries on hosts that can never be reached from the public internet were removed in September 2026 and are refused at write time. - Immediate removal of a live entry (for example a path you have retired but still serve) is still a manual request — reach out on Discord or open an issue with proof of resource ownership. The x402 specification defines no delisting semantics yet.
Troubleshooting
Need help?
Join our Community
Have questions or want to connect with other developers? Join our Discord server.

