Skip to main content
This guide picks up where the x402 Quickstart Guide leaves off. You’ll make a paid endpoint discoverable, build an agent that finds a paid resource through a facilitator’s Bazaar and pays for it (within a spending cap), and look at what changes when you move to mainnet. Everything here uses the stock x402 packages.

Before you start

You’ll need Node.js and two testnet accounts (a buyer and a seller), each with a USDC trustline. The buyer’s account also needs some testnet USDC. The quickstart walks through setting these up. The commands below were tested with Node.js 24 and @x402/* 2.30.0.

Networks, assets, and trustlines

A dollar price such as "$0.01" is converted to USDC on whichever network you register. On stellar:testnet, that’s amount: "100000" of the testnet USDC contract; on stellar:pubnet, it’s the same amount of the mainnet contract. To charge in a different SEP-41 token, set the price as { asset: "<contract id>", amount: "<base units>" } instead (the facilitator must support that asset). A classic G… account needs a USDC trustline before it can hold USDC. That goes for both the buyer (who sends USDC) and the seller’s payTo account (which receives it). If payTo doesn’t have a trustline, the payment fails. Contract addresses (C…, such as a smart wallet) don’t need trustlines. Every facilitator publishes what it supports at /supported. Look for "scheme": "exact" on your network with "extra": {"areFeesSponsored": true}. A facilitator that catalogs resources for discovery also lists "bazaar" in its top-level extensions.

Make an endpoint discoverable

The declareDiscoveryExtension function describes a route’s input and output. A Bazaar-enabled facilitator uses that description to catalog the route, and agents use it to call the route without needing to read your docs.
seller.js
Sellers don’t need to register anywhere. When a payment carrying the bazaar extension settles through a Bazaar-enabled facilitator, that facilitator may add the resource to its catalog. Facilitators validate the metadata first, and some refuse resources on non-public hosts (like localhost). The result comes back in the EXTENSION-RESPONSES header as success, processing, or rejected (with a reason), and the stock HTTPFacilitatorClient logs it. Either way, the payment still settles. To get listed, deploy your seller on a public hostname.

Build an agent that discovers and pays

Facilitators that implement the x402 Bazaar extension can serve their catalog at GET /discovery/resources and GET /discovery/search:
The withBazaar helper from @x402/extensions/bazaar adds those same discovery calls to the stock facilitator client. The agent below searches the catalog, keeps only results it can pay for on testnet at 0.05 USDC or less, fills in the listing’s example input, and then pays. As a backstop, spendControls refuses any payment above the cap before anything gets signed.
agent.js
Set DISCOVERY_URL to any facilitator whose /supported endpoint lists the bazaar extension. The community facilitators list notes which ones catalog resources. The receipt’s transaction field is the settlement transaction hash. Look it up on a block explorer to see the buyer’s USDC move to payTo, with the fee paid by the facilitator.
Listings are supplied by sellers, so treat them as untrusted data. Check the price, network, and payTo before paying. The agent above checks the price and network, and spendControls enforces the cap again at signing time.

What the payer signs

The buyer signs a Soroban authorization entry for exactly one call: transfer(from = buyer, to = payTo, amount) on the asset contract named in the payment requirements. Changing the recipient, the amount, or the asset invalidates the signature. The entry expires after roughly maxTimeoutSeconds (its expiration ledger is the current ledger plus maxTimeoutSeconds divided by the ledger close time), so an unsettled payment can’t be settled later. And since the buyer isn’t the transaction source, paying doesn’t consume the buyer’s sequence number.

Fee sponsorship

When extra.areFeesSponsored is true, the facilitator pays the network fee for the settlement transaction, so the buyer doesn’t need any XLM to cover it. This is fee sponsorship only, not sponsored reserves. A classic account still needs its own base reserve plus the reserve for its USDC trustline (see Lumens).

Moving to mainnet

  • Network. Use stellar:pubnet on both sides. A "$0.01" price then resolves to mainnet USDC.
  • Facilitator. Choose one whose /supported endpoint lists exact on stellar:pubnet. Some require an API key, which the seller sends using the createAuthHeaders option on HTTPFacilitatorClient.
  • RPC. On mainnet, the stock client has no default RPC endpoint and throws an error if you don’t provide one. Pick one from the RPC providers list and pass it in: new ExactStellarScheme(signer, { url: "<mainnet RPC URL>" }).
  • Accounts. The buyer and any G… payTo account need a trustline to mainnet USDC (issuer GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN), plus enough XLM for their reserves.
  • Keys and limits. Keep secret keys out of your code, give agents a separate low-balance account, and set spendControls to the most you’re willing to lose on a single payment.

Troubleshooting

Learn more