Skip to content

docs(x402): add a guide for getting an x402 endpoint discovered by agents - #2344

Open
GigaHierz wants to merge 1 commit into
mainfrom
docs/x402-get-discovered
Open

GigaHierz wants to merge 1 commit into
mainfrom
docs/x402-get-discovered

Conversation

@GigaHierz

Copy link
Copy Markdown
Contributor

The hole, and the fix

A seller who follows the x402 page ends up with a working 402 endpoint that nothing lists. There was no page saying how agents find an endpoint, and the indexes disagree: one crawls an origin you submit, one lists only endpoints that settle through its own facilitator (which does not settle on Celo), one accepts Base and Solana resources only, one reads ERC-8004 identities, and Buy's catalog is curated.

This adds build-on-celo/build-with-ai/x402-get-discovered.mdx (Guide type per AGENTS.md: Prerequisites, How it works, task sections, Troubleshooting, Resources, Related), registers it in docs.json after the x402 page, and links it from the x402 page's Related list. The code example registers the bazaar extension from @x402/extensions and declares one route; it was run as written (module script, node server.mjs, unpaid GET /weather?city=Lagos against https://api.x402.celo.org) and returned 402 with extensions.bazaar.info exactly as shown in the page's JSON block. mint broken-links reports success no broken links found.

What this does NOT do / residual risk

  • It does not document a discovery endpoint on the Celo facilitator, because none is exposed; the page only says what each existing index requires.
  • The index rules are external facts as of 2026-09-28: Coinbase's supported-network list and 30-day pruning come from docs.cdp.coinbase.com/x402/seller/get-discovered and /x402/network-support; the x402scan Base/Solana rule comes from its registration behaviour (open issue [Ogoyi Thompson]-Sage-Creating-a-Smart-Contract-for-Secure-and-Efficient-Plant-Seedling-Sales-on-the-Celo-Blockchain #1012 in Merit-Systems/x402scan); the agent402.tools endpoint from its seller page. Any of them can change without notice; the page cites each source in Resources.
  • The Buy catalog step points at an issue form that lands with celo-org/buy-skill PR (see Refs); until that merges, the link opens the existing issue chooser.

Judgement calls

  • A separate page rather than a section: the x402 page is already 263 lines and this is a different task (getting found, not getting paid). Cheap to fold back in later.
  • The example keeps the createAuthHeaders credential block from the x402 page for consistency even though the 402 itself does not need it; the verified run omitted it and produced the same 402.
  • Route-level serviceName, tags and iconUrl from the spec are not shown because their placement in the express route config was not verified.

Issues

Closes #
Refs celo-org/buy-skill listing-request form PR, celo-org/celopedia-skills seller-discoverability PR

Stacking / conflicts

Branched off main, independent of other open PRs. Touches docs.json (one inserted line) and one Related bullet in x402.mdx.

🤖 Generated with Claude Code

…ents

New Guide page under Agent Infrastructure: the bazaar discovery extension
(example run against api.x402.celo.org), and the entry rule for each index
(agent402.tools, Coinbase's discovery index, x402scan, 8004scan, the Buy
catalog). Linked from the x402 page's Related list and added to docs.json.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@GigaHierz
GigaHierz requested a review from a team as a code owner September 28, 2026 11:55

@palango palango left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful page, and most of the external claims check out. The server example doesn't type-check against @x402/* 2.27.0 (tsc --noEmit --strict, module nodenext), so readers who paste it into a .ts file hit two errors. Details and fixes inline, plus a few accuracy points. The fixed version compiles and still emits "method": "GET" in extensions.bazaar.info.input.

.register("eip155:42220", new ExactEvmScheme())
.registerExtension(bazaarResourceServerExtension);

const routes = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without a RoutesConfig annotation, network widens to string and paymentMiddleware(routes, server) fails: Type 'string' is not assignable to type '${string}:${string}'. The x402 page keeps this annotation for the same reason. Suggest importing it on L38 (import { HTTPFacilitatorClient, type RoutesConfig } from "@x402/core/server";) and writing const routes: RoutesConfig = {.

description: "Current weather for a city",
mimeType: "application/json",
extensions: declareDiscoveryExtension({
method: "GET",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method isn't part of DeclareDiscoveryExtensionInput in 2.27.0 (the type omits it), so this is TS2353: 'method' does not exist in type 'DeclareDiscoveryExtensionInput'. bazaarResourceServerExtension fills the method in from the route key, so this line can simply go.

}
```

Write the `description` for an agent that is deciding whether to call you: what the endpoint returns and when to use it, in under 500 characters. For a `POST` route, pass `method: "POST"`, `bodyType: "json"`, and the example body as `input`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as L69: passing method: "POST" fails to type-check. Suggest: the method comes from the route key ("POST /path"); pass bodyType: "json" and the example body as input.

app.listen(3000);
```

An unpaid `GET /weather?city=Lagos` returns `402` with the price in `accepts` and the description in `extensions.bazaar`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running this, the 402 response body is {}. The JSON below arrives base64-encoded in the PAYMENT-REQUIRED response header, and decoded it matches this block. Someone curling the endpoint will see {} and think the extension isn't working, so worth saying this is the decoded PAYMENT-REQUIRED header.

Coinbase's index adds an endpoint automatically after a paid call has settled through Coinbase's facilitator, and drops it after 30 days without one. That facilitator settles on Base, Base Sepolia, Polygon, Arbitrum, World and Solana. To appear there with a Celo offer:

1. Follow [Coinbase's seller guide](https://docs.cdp.coinbase.com/x402/seller/get-discovered) to add an offer on one of those networks and settle it through Coinbase's facilitator.
2. Keep the `eip155:42220` offer in the same `accepts` list. The index shows every offer the endpoint declares.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a source for "The index shows every offer the endpoint declares" (and the matching "An offer for eip155:42220 is shown next to the offer that settled there" on L25)? Coinbase's get-discovered page doesn't say non-settling offers are shown. If this was observed on a live listing, a link would help; otherwise I'd cut both claims, since the Coinbase section rests on them.

|---|---|---|
| The 402 has no `extensions` block | The extension is not registered on the server, or the route has no `extensions` entry | Call `registerExtension(bazaarResourceServerExtension)` and add `extensions: declareDiscoveryExtension(...)` to the route |
| agent402.tools shows nothing after an hour | The origin needs authentication before it answers 402, or it is not reachable from the public internet | Return the 402 to an unauthenticated request; check the origin from outside your network |
| A Coinbase listing disappeared | No settlement through Coinbase's facilitator for 30 days, or a failed health probe | Make a paid call on the offer that settles there |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per Coinbase's get-discovered doc, a failed health probe drops an endpoint from the curated/featured tier but it stays in the Bazaar. Removal comes from 30 days without a settlement or the endpoint no longer returning 402. Suggest replacing "or a failed health probe" with the latter.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants