Skip to main content
The Bazaar is the PayAI facilitator’s public catalog of x402 resources. AI agents and clients browse it via /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:
  1. Your server declares. Your 402 response includes a bazaar discovery declaration describing the endpoint (method, input/output shape, service metadata).
  2. 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.
  3. The facilitator catalogs. On /verify and /settle, the facilitator extracts the declaration from the payment payload and upserts your catalog entry asynchronously.
  4. 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, then GET on 404/405; the probe never sends POST, PUT, PATCH or DELETE). A resource is listed when either proof holds: the indexing payment was a settlement (a buyer only pays after receiving your 402), or the read-only probe itself answers 402 Payment Required. A verify-only first listing of a POST/PUT/PATCH/DELETE or MCP resource (or one with no declared method) is deferred with resource_needs_settlement until its first settled payment; a GET resource answering something other than 402 unpaid is refused as resource_not_x402. Unreachable hosts, GET paths answering 404/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.
Since 2026-07-29, cataloging runs on /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 the bazaar 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:
The optional 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 absolute http 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 extensionaccepts, schemas, metadata, and lastUpdated all 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/GET requests (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, or 404/405/410 on a GET/HEAD resource — is hidden from /discovery/resources and /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. A 404/405 from a POST/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 — /verify catalogs and moves no funds. Confirm with the processing header 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:
An MCP server with N tools contributes N catalogEntries but one resource and at most one new host.

Opting out and delisting

  • v1: declare discoverable: false inside outputSchema.input and the facilitator will not index the resource.
  • v2: simplest is to omit the bazaar extension 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/410 on a GET resource) 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.