Skip to content

docs(x402): define facilitator at first use and add a check-that-it-works step - #2397

Open
GigaHierz wants to merge 3 commits into
mainfrom
docs/x402-facilitator-checkpoint
Open

GigaHierz wants to merge 3 commits into
mainfrom
docs/x402-facilitator-checkpoint

Conversation

@GigaHierz

@GigaHierz GigaHierz commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Part of #2263 (second PR).

Two gaps on the x402 page from the ticket: facilitator was used without a definition, and the Celo setup had no point where a reader could tell it worked.

Changes

  • One plain sentence defining facilitator at its first use, under "How it works". It says the seller does not run a node or need CELO for gas, and does not imply settlement is free.
  • New "Check that it works" section after "Getting an API Key": the unpaid curl, the 402 with an empty {} body, and a decode command for the base64 PAYMENT-REQUIRED header that shows 10000 base units (USDC has 6 decimals, so $0.01). This check needs no API key.
  • The paid half points at the Celo Composer x402 template's own seller and buyer (pnpm api:dev, pnpm api:buy), which run on Celo Sepolia with test USDC.
  • For readers who switch the page's own server to Celo Sepolia, it says to change the facilitator url to https://api.x402.sepolia.celo.org as well as the network, asset and payTo. The mainnet facilitator only lists eip155:42220, so the server fails on startup otherwise.

The base-unit comments (amount: "10000", // $0.01 — USDC has 6 decimals) the ticket asked for are already on the page, so nothing changes there. The ticket's maxValue: "1000000" example is no longer on the page.

Verification

  • mint validate passes on this branch; mint broken-links passed earlier, and the CI failure was the Mintlify CLI crashing, not a link problem.
  • Run in review against the example server (@x402/express 2.20.0, no key set; not by me in this environment): curl -i returns 402, the body is {}, and the decode command prints "amount":"10000".
  • Checked that https://api.x402.sepolia.celo.org/supported lists eip155:11142220 and https://api.x402.celo.org/supported lists only eip155:42220. Checked the Composer buyer defaults to testnet and pays its own POST /paid route.
  • The Composer template's own flow (create --template x402, pnpm api:dev, pnpm api:buy) run on Celo Sepolia: /free answers, the buyer gets HTTP 200 with {"message":"You paid for this response.",...} and a settlement receipt, tx 0x7cd73324…3c00. This is the output the page tells readers to expect. pnpm install needed the public npm registry here because of a local registry auth setting, which is unrelated to the template.
  • Paid path run end to end on live Celo Sepolia: the page's Express block with the Celo Sepolia network, USDC asset, a payTo address I control and the Sepolia facilitator URL, plus the metering key, and a buyer built on @x402/fetch. The unpaid request returned 402 with an empty body and a decoded amount of 10000; the paid request returned 200 with {"data":"paid content"} and a settlement receipt, tx 0x9b59c880…665f21.

🤖 Generated with Claude Code

@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.

The facilitator sentence is in the right place and the 402 check does what it says. I ran the server block from this branch with @x402/express 2.20.0, and curl -i http://localhost:3000/premium returns 402 Payment Required even with no API key set.

Two things keep the step from working as the checkpoint #2263 asked for. The response body is {} and the price only appears in the base64 PAYMENT-REQUIRED header, so a reader won't see 10000 anywhere. And the paid half has no buyer on this page, the route is on mainnet, and payTo is still a placeholder, so nobody can get to "you have charged an agent $0.01" from here. Pointing at the buyer script in the celo-composer x402 template, or adding a short Celo Sepolia buyer snippet, would close that gap. Details inline.

#2396 and #2398 merge cleanly on top of this, so the order doesn't matter.

Comment thread build/agents/x402.mdx Outdated

## How It Works

A *facilitator* is a service that checks a buyer's signed payment and submits it on-chain on the seller's behalf, so the seller does not run a node or hold gas.

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.

Small accuracy point: the Celo facilitator pays gas up front but bills it back to the seller's balance (gas at cost plus 0.3% after the free credits, see /api/config and the paragraph under Getting an API Key). "so the seller does not run a node or need CELO for gas" avoids suggesting settlement is free.

Comment thread build/agents/x402.mdx Outdated
curl -i http://localhost:3000/premium
```

The response is `402 Payment Required`, and the payment requirements describe the route's price: `10000` base units of USDC, which is $0.01 because USDC has 6 decimals. That `402` means the middleware is wired to the route.

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 against the example server gives a {} body. The requirements are only in the base64 PAYMENT-REQUIRED header, so 10000 never shows up in the curl -i output. Could you either say that, or add a decode step? For example:

curl -s -o /dev/null -D - http://localhost:3000/premium | awk -F': ' 'tolower($1)=="payment-required"{print $2}' | tr -d '\r' | base64 -d

It's also worth saying that this check needs no API key.

Comment thread build/agents/x402.mdx Outdated

The response is `402 Payment Required`, and the payment requirements describe the route's price: `10000` base units of USDC, which is $0.01 because USDC has 6 decimals. That `402` means the middleware is wired to the route.

A buyer that signs the authorization and retries the request receives `200` with `{"data":"paid content"}`, and the payment settles on Celo for the price above. At that point you have charged an agent $0.01. Settlement needs the API key from the next section.

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.

This is the checkpoint the ticket asked for, but the page gives the reader no way to be the buyer. There's no client example here, the route is mainnet with real USDC, and payTo is still 0xYourSellerPayoutAddress. Could we link the buyer script from npx @celo/celo-composer@latest create --template x402 (see /build/quickstart), or add a short @x402/fetch buyer against Celo Sepolia? I'd also move this paragraph after Getting an API Key, since it depends on it.

GigaHierz and others added 2 commits October 19, 2026 11:04
…orks step (#2263)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Show how to read the payment requirements from the PAYMENT-REQUIRED header,
say the check needs no API key, point to a buyer, move the step after the
API key section and correct the gas wording.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@GigaHierz
GigaHierz force-pushed the docs/x402-facilitator-checkpoint branch from 6099c39 to 56119a5 Compare October 19, 2026 09:05
@GigaHierz

Copy link
Copy Markdown
Contributor Author

Rebased and reworked: facilitator sentence now says the seller does not need CELO for gas; the check explains the empty body and the base64 PAYMENT-REQUIRED header, with the decode command, and says it needs no API key; it moved after Getting an API Key; the paid half points at the Celo Composer x402 buyer (quickstart) and the Celo Sepolia values. I did not write a new buyer snippet. The paid-request path is still untested here.

@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.

Thanks, the facilitator sentence and the decode step are what I asked for. I re-ran the server block from this head with @x402/express 2.20.0 and no API key. curl -i gives a 402 with a {} body, and the decode command prints the requirements with "amount":"10000".

The paid half still doesn't work if a reader follows it. If you switch only the network and payTo to Celo Sepolia, the server crashes on startup, because the block still points at https://api.x402.celo.org and that facilitator's /supported lists only eip155:42220. Separately, the Composer buyer sends POST to localhost:3001/paid, but this route is GET /premium on port 3000, so the buyer gets a 404 here, not a 402. Details inline.

Could you also update the PR body? It still says the section follows the resource-server example and that the curl check wasn't run.

The broken-links failure in CI is the Mintlify CLI crashing (The onCancel handler was attached after the promise settled), not a link problem, and mint broken-links passes locally, so a re-run should clear it. #2396 and #2398 still merge cleanly on top.

Comment thread build/agents/x402.mdx Outdated

The decoded JSON shows the route's price: `10000` base units of USDC, which is $0.01 because USDC has 6 decimals. A `402` with that price means the middleware is wired to the route.

To see a paid response you need a buyer that signs the authorization and retries the request. The `x402` template of Celo Composer includes a buyer script that pays an x402 endpoint in USDC; see [x402 paid API](/build/quickstart#x402-paid-api). The route above uses Celo mainnet and a placeholder `payTo`, so point both at a Celo Sepolia route and an address you control first, using the Celo Sepolia values under [Celo Configuration](#celo-configuration). A paid request returns `200` with `{"data":"paid content"}` and the payment settles on Celo for the price above. At that point you have charged an agent $0.01. Settlement needs the API key from the previous section.

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.

Following this as written crashes the server. The block above hardcodes url: "https://api.x402.celo.org", and that facilitator's /supported only lists eip155:42220. With just the network, asset and payTo switched to Celo Sepolia, startup fails with:

RouteConfigurationError: x402 Route Configuration Errors:
  - Route "GET /premium": Facilitator does not support scheme "exact" on network "eip155:11142220"

It works once the URL is https://api.x402.sepolia.celo.org. Could this sentence say to change the facilitator URL too?

Also, the Composer buyer (templates/x402/apps/api/src/buyer.ts.hbs) calls payFetch(url, { method: "POST" }) against SELLER_URL, which defaults to http://localhost:3001/paid. Pointed at this server it gets a 404, because only GET /premium exists here. Either tell readers to run the template's own seller and buyer (pnpm api:dev, then pnpm api:buy) and expect its response rather than {"data":"paid content"}, or tell them to set SELLER_URL=http://localhost:3000/premium and switch the buyer to GET. Minor: on Sepolia it's test USDC, so "charged an agent $0.01" could say so.

… Composer buyer to its own seller

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@GigaHierz

Copy link
Copy Markdown
Contributor Author

Pushed the fixes from the last round: the Celo Sepolia switch now includes the facilitator URL (https://api.x402.sepolia.celo.org, checked against /supported), the paid half points at the Composer template's own seller and buyer (pnpm api:dev, pnpm api:buy) and says to expect its output, the Sepolia charge is called test USDC, and the PR body is updated. mint validate passes. The paid request on Sepolia is still not run end to end.

@GigaHierz

Copy link
Copy Markdown
Contributor Author

Ran the paid path end to end on live Celo Sepolia: unpaid 402 with the decoded 10000, then a paid 200 with a settlement receipt (https://celo-sepolia.blockscout.com/tx/0x9b59c8809816ab5277b390880a82e98788ed96e4b922f80d239b782543665f21). Body updated.

@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.

Thanks, this covers everything from both rounds. I re-ran the server block from this head with @x402/express 2.20.0, switched to Celo Sepolia the way the new paragraph says. It starts, curl -i gives a 402, and the decode command prints "amount":"10000" on eip155:11142220. With the mainnet facilitator URL left in place it still fails at startup with a RouteConfigurationError, so the warning is right. The Composer pointer matches the template: api:dev and api:buy exist, it defaults to the Sepolia facilitator, and the buyer pays its own POST /paid. Both settlement txs in the PR body succeeded on Celo Sepolia.

One optional suggestion inline. Approving.

Comment thread build/agents/x402.mdx

The decoded JSON shows the route's price: `10000` base units of USDC, which is $0.01 because USDC has 6 decimals. A `402` with that price means the middleware is wired to the route.

To see a paid response you need a buyer that signs the authorization and retries the request. The simplest way is the `x402` template of Celo Composer, which ships its own seller and buyer wired to Celo Sepolia (see [x402 paid API](/build/quickstart#x402-paid-api)). Run the seller with `pnpm api:dev` and the buyer with `pnpm api:buy`. The buyer pays the template's own route and prints the response and the settlement transaction, so expect that output rather than the `{"data":"paid content"}` from the server above. The buyer wallet needs test USDC on Celo Sepolia and no CELO.

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.

Optional: the template's seller only charges once X402_PAY_TO is set in apps/api/.env. Without it, /paid runs for free (index.ts.hbs warns "/paid is running FREE") and the buyer gets no settlement tx. A short "fill in apps/api/.env as the template's X402_SETUP.md describes" before pnpm api:dev would save someone that detour.

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