Skip to main content

agentic-payments

Agentic and machine-to-machine payments on Stellar. Covers x402 (HTTP 402 paid APIs through a facilitator, fee-sponsored clients; the guide configures OZ Channels) and MPP (Machine Payments Protocol) in both Charge mode (per-request SAC) and Session mode (channel-backed off-chain commits, high-frequency; formerly called Channel mode). Defaults to USDC (SEP-41 SAC) on `stellar:testnet`/`stellar:pubnet` (CAIP-2). Use when selling a paid API to AI agents, building an x402 client, or designing a payment-channel architecture for high-frequency agent traffic.

Source facts

Repository
stellar/stellar-docs
Last source activity
November 9, 2026 at 15:34
Detected SKILL.md language
English
Stars
145
Forks
327

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
agentic-payments
description
Agentic and machine-to-machine payments on Stellar. Covers x402 (HTTP 402 paid APIs through a facilitator, fee-sponsored clients; the guide configures OZ Channels) and MPP (Machine Payments Protocol) in both Charge mode (per-request SAC) and Session mode (channel-backed off-chain commits, high-frequency; formerly called Channel mode). Defaults to USDC (SEP-41 SAC) on `stellar:testnet`/`stellar:pubnet` (CAIP-2). Use when selling a paid API to AI agents, building an x402 client, or designing a payment-channel architecture for high-frequency agent traffic.
user-invocable
true
argument-hint
[payment task]
source
https://github.com/stellar/stellar-dev-skill/tree/fb63412da70102edb527baf4cf3068f25713cd14/skills
# Agentic Payments: x402 + MPP Two complementary protocols for AI-agent and machine-to-machine payments on Stellar. Pick based on who depends on whom and how often the agent pays. ## Quick decision | | x402 | MPP Charge | MPP Session | |--|------|------------|-------------| | Per-request on-chain tx? | Yes (via facilitator) | Yes (SAC) | No (off-chain commits) | | Needs facilitator? | Yes — hosted or your own ([options](#facilitator-options)) | No | No | | Client needs XLM? | No (fees sponsored) | Optional (`feePayer`) | Yes | | Setup complexity | Low | Low | Medium (deploy contract first) | | Best for | Quickest setup, fee-free clients | No third-party dep | High-frequency agents | - Selling an API, want zero-XLM clients → **x402 Seller** in [x402 — Paid APIs + Agent Buyer Clients](#x402--paid-apis--agent-buyer-clients) - Calling an x402 API from an agent → **x402 Buyer** in [x402 — Paid APIs + Agent Buyer Clients](#x402--paid-apis--agent-buyer-clients) - Selling an API, no facilitator dependency → **Charge mode** in [MPP — Machine Payments Protocol (Charge + Session)](#mpp--machine-payments-protocol-charge--session) - Agent making many requests per session → **Session mode** in [MPP — Machine Payments Protocol (Charge + Session)](#mpp--machine-payments-protocol-charge--session) - Sold an API, now want agents to find it → **Discovery** in [mpp.md](#discovery-let-agents-find-your-paid-api) - Unsure → x402 (lowest friction to get started) All protocols use USDC (SEP-41 SAC) by default; `stellar:testnet` / `stellar:pubnet` CAIP-2 network IDs. ## Read the file that matches the task This file carries the decision table, the shared testnet account setup, and the USDC address reference. The protocol playbooks live alongside it: | Task | Section | |------|------| | Sell a paid API via a facilitator (zero-XLM clients), build an x402 buyer agent | [x402 — Paid APIs + Agent Buyer Clients](#x402--paid-apis--agent-buyer-clients) | | Facilitator-free per-request payments (Charge) or channel-backed sessions (Session) | [MPP — Machine Payments Protocol (Charge + Session)](#mpp--machine-payments-protocol-charge--session) | | Publish an OpenAPI discovery document so agents find your paid API | [Discovery](#discovery-let-agents-find-your-paid-api) | | Create/fund testnet accounts, add USDC trustlines, get testnet USDC | [Testnet setup](#testnet-setup-shared) (below) | | Which USDC address goes where (classic issuer vs SAC) | [Two USDC addresses](#two-usdc-addresses-dont-confuse-them) (below) | ## Related skills - The SACs the protocols call → `/.well-known/agent-skills/smart-contracts/skill.md` - USDC and other classic assets → `/.well-known/agent-skills/assets/skill.md` - Wallets and signing in the buyer client → `/.well-known/agent-skills/dapp/skill.md` - RPC simulation / submission patterns → `/.well-known/agent-skills/data/skill.md` - SEP-41 (token interface) and related standards → `/.well-known/agent-skills/standards/skill.md` ## Testnet setup (shared) Both protocols need the same base setup: a **client/payer** account (signs and pays from a USDC balance) and a **server/recipient** account. Both need a USDC trustline. One step of this shared setup is web-only (Captcha) and cannot be scripted: the Circle USDC faucet. The rest of it can be automated — [x402 — Paid APIs + Agent Buyer Clients](#x402--paid-apis--agent-buyer-clients) ships a `setup.js` that does steps 1–3 and writes a starter `.env`. (Outside this setup, the OZ Channels facilitator that x402.md configures has its own web-only key generator. Each facilitator sets its own auth requirement — the x402.org one needs none — and MPP needs no third party at all.) 1. **Generate two keypairs** ```bash node -e "const { Keypair } = require('@stellar/stellar-sdk'); for (const n of ['RECIPIENT','PAYER']) { const k = Keypair.random(); console.log(n, k.publicKey(), k.secret()); }" ``` 2. **Fund both with testnet XLM (friendbot)** ```bash curl "https://friendbot.stellar.org?addr=RECIPIENT_G..." curl "https://friendbot.stellar.org?addr=PAYER_G..." ``` 3. **Add a USDC trustline to BOTH accounts** — open [Stellar Lab](https://lab.stellar.org/account/fund?network=test) and add a USDC trustline to each `G...`, or run via SDK for each keypair: ```js import * as StellarSdk from "@stellar/stellar-sdk"; const horizon = new StellarSdk.Horizon.Server("https://horizon-testnet.stellar.org"); // Circle's classic USDC issuer on Stellar testnet const USDC_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"; async function addTrustline(secret) { const kp = StellarSdk.Keypair.fromSecret(secret); const acc = await horizon.loadAccount(kp.publicKey()); const tx = new StellarSdk.TransactionBuilder(acc, { fee: StellarSdk.BASE_FEE, networkPassphrase: StellarSdk.Networks.TESTNET, }) .addOperation(StellarSdk.Operation.changeTrust({ asset: new StellarSdk.Asset("USDC", USDC_ISSUER), })) .setTimeout(60) .build(); tx.sign(kp); return horizon.submitTransaction(tx); } // Repeat for both the recipient secret and the payer secret. await addTrustline(process.env.RECIPIENT_SECRET); await addTrustline(process.env.PAYER_SECRET); ``` Without a trustline on the recipient, the SAC `transfer` settles into nothing and the request fails with `op_no_trust`. 4. **Fund the PAYER with testnet USDC** — open the [Circle testnet faucet](https://faucet.circle.com/), select **Stellar testnet**, paste the payer's `G...`. Web Captcha; no API. ## Two USDC addresses (don't confuse them) USDC on Stellar has two addresses, used in different places. Mixing them up is a common stumble. | Address | Format | Used for | |---------|--------|----------| | Classic asset issuer | `G...` (32-byte ed25519 public key) | The `issuer` of the classic USDC asset; used when adding a trustline (`new Asset("USDC", G...)`) | | SAC (Stellar Asset Contract) | `C...` (32-byte contract address) | The contract the protocol invokes `transfer` on; used in payment requirements | Use the exported constants instead of hard-coding when possible: ```js import { USDC_TESTNET_ADDRESS, USDC_PUBNET_ADDRESS } from "@x402/stellar"; // USDC_TESTNET_ADDRESS = "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA" // USDC_PUBNET_ADDRESS = "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75" ``` x402's `payTo` route config and MPP's `recipient` are always a classic account (`G...`). The SAC address only appears where the config names the settlement asset (x402's custom `asset` price config, MPP's `currency`). --- # x402 — Paid APIs + Agent Buyer Clients Sell paid APIs to AI agents and build agent buyer clients over HTTP 402, settled in USDC through an x402 facilitator. This guide configures the OpenZeppelin Channels facilitator; [Facilitator options](#facilitator-options) below lists the alternatives. Companion to [SKILL.md](#agentic-payments-x402--mpp) (decision table, shared testnet setup, USDC addresses); the facilitator-free alternative lives in [MPP — Machine Payments Protocol (Charge + Session)](#mpp--machine-payments-protocol-charge--session). ## When to use x402 x402 is the right choice when: - You want the fastest path to a paid API — minimal code, no contract deployment - You want clients (including AI agents) to pay with **zero XLM** — a hosted facilitator sponsors the network fees (OZ Channels on both networks, the x402.org facilitator on testnet) - You're building on top of an existing x402 ecosystem (Coinbase, other chains) Trade-off: you depend on a facilitator for verification and settlement, and each hosted one is its own third party. If you need zero third-party dependency, either self-facilitate in your own process ([Facilitator options](#facilitator-options)) or use MPP Charge ([MPP — Machine Payments Protocol (Charge + Session)](#mpp--machine-payments-protocol-charge--session)) instead. ## How x402 works on Stellar ``` Client → GET /resource → Server Client ← 402 Payment Required (payment requirements) ← Server Client builds SAC USDC transfer Client signs auth entries only (not the full tx envelope) Client → GET /resource + X-PAYMENT header → Server Server → facilitator /verify + /settle → Stellar (~5s) Client ← 200 OK + resource ``` The key Stellar difference: clients sign **auth entries**, not full transaction envelopes. The facilitator assembles the transaction, pays fees, and submits. Clients need zero XLM. ## Facilitator options x402 is permissionless — anyone can run a facilitator, and the protocol itself never requires an API key. Pick the facilitator before you write the server: - **OZ Channels** (`https://channels.openzeppelin.com/x402/testnet`, `https://channels.openzeppelin.com/x402`) — testnet and mainnet, API key required on both. Stellar's docs list it as the *Build on Stellar Relayer* with the OpenZeppelin x402 plugin. **This guide configures it**, and it is the mainnet facilitator those docs name. - **The x402.org facilitator** (`https://x402.org/facilitator`, also `@x402/core`'s `DEFAULT_FACILITATOR_URL`) — testnet only, no API key. Stellar's docs call it the Coinbase x402 facilitator. Its `/supported` response advertises `stellar:testnet` with sponsored fees and carries no `stellar:pubnet` entry. - **AgentSmith Facilitator** (`https://testnet.agentsmith.xyz/facilitator`, `https://mainnet.agentsmith.xyz/facilitator`) — testnet and mainnet, no API key required on testnet; mainnet requires a Resource Server API key, which the mainnet `/supported` check also needs. Sponsors XLM fees on both networks. Supports `exact` and experimental `upto` schemes. Docs: [agentsmith.xyz/docs.html](https://agentsmith.xyz/docs.html). - **Your own** — run a facilitator, or facilitate in process. The [self-facilitation example](https://github.com/x402-foundation/x402/tree/main/examples/typescript/servers/self-facilitation) shows the shape, an `x402Facilitator` from `@x402/core/facilitator` wired into `x402ResourceServer`, but it registers the EVM scheme on Base Sepolia. On Stellar you register `ExactStellarScheme` from `@x402/stellar/exact/facilitator`, built with the `FacilitatorStellarSigner` it settles from (`createEd25519Signer` on that account's `S...` key). That account submits the transaction and pays the XLM fees, so there is no third party and no external fee sponsor. `GET <facilitator-url>/supported` is the authoritative check for whether an operator serves your network. The full lists live on [x402's facilitators page](https://docs.x402.org/dev-tools/facilitators) and in the [x402 Facilitators section](https://developers.stellar.org/docs/build/agentic-payments/x402#x402-facilitators) of Stellar's docs. ## Seller: monetize an Express API ```bash npm install @x402/express @x402/core @x402/stellar @stellar/stellar-sdk express dotenv npm pkg set type=module ``` ```js // server.js import "dotenv/config"; import express from "express"; import { paymentMiddleware, x402ResourceServer } from "@x402/express"; import { HTTPFacilitatorClient } from "@x402/core/server"; import { ExactStellarScheme } from "@x402/stellar/exact/server"; // Drive the CAIP-2 network ID from one place. Switching to mainnet means // flipping STELLAR_NETWORK and FACILITATOR_URL in .env, nothing in code. const NETWORK = process.env.STELLAR_NETWORK || "stellar:testnet"; // OZ Channels is this example's facilitator, and it is the key's owner: // the API key is that provider's requirement, not x402's. Drop this check // and createAuthHeaders below if you point FACILITATOR_URL at a // facilitator that needs no auth. if (!process.env.OZ_API_KEY) { throw new Error( "OZ_API_KEY is required by the OZ Channels facilitator. Generate one at https://channels.openzeppelin.com/testnet/gen (testnet) or https://channels.openzeppelin.com/gen (mainnet)." ); } const facilitator = new HTTPFacilitatorClient({ url: process.env.FACILITATOR_URL || "https://channels.openzeppelin.com/x402/testnet", // OZ Channels requires Bearer auth on both testnet and mainnet createAuthHeaders: async () => { const h = { Authorization: `Bearer ${process.env.OZ_API_KEY}` }; return { verify: h, settle: h, supported: h }; }, }); const resourceServer = new x402ResourceServer(facilitator) .register(NETWORK, new ExactStellarScheme()); const app = express(); app.use( paymentMiddleware( { "GET /weather": { accepts: { scheme: "exact", price: "$0.001", // human-readable, auto-converts to 7-decimal USDC units network: NETWORK, payTo: process.env.STELLAR_RECIPIENT, // recipient G... account }, description: "Current weather data", }, }, resourceServer ) ); app.get("/weather", (_req, res) => { res.json({ city: "San Francisco", temp: 18, conditions: "Foggy" }); }); app.listen(3001, () => console.log(`x402 server on http://localhost:3001 (${NETWORK})`)); ``` **Env vars:** - `STELLAR_NETWORK` — CAIP-2 network ID; defaults to `stellar:testnet`. Set to `stellar:pubnet` for mainnet. - `STELLAR_RECIPIENT` — your G... address (receives USDC, needs a USDC trustline) - `OZ_API_KEY` — OZ Channels API key (**that facilitator requires it on both testnet and mainnet**; generate at the link in the runbook below). Another facilitator brings its own auth, or none. - `FACILITATOR_URL` — defaults to the OZ Channels testnet URL above; set to `https://channels.openzeppelin.com/x402` for mainnet **Price format options:** - `"$0.001"` — human-readable, auto-converts to 7-decimal USDC units - `{ amount: "1000", asset: "ASSET_SAC_CONTRACT_ID" }` — explicit base units for non-USDC assets **`payTo` is the recipient's classic Stellar account (`G...`), not the USDC SAC contract address.** Sending USDC lands in the classic balance of the `payTo` account, which is why that account also needs a USDC trustline. The SAC contract address is what the protocol invokes `transfer` on; see [Two USDC addresses](#two-usdc-addresses-dont-confuse-them) in the router. ## Pricing multiple routes with `paymentMiddlewareFromConfig` The seller example above prices a single route through `paymentMiddleware` and `x402ResourceServer`. For more than one paid route, `@x402/express` also exports `paymentMiddlewareFromConfig`, which takes the same route-keyed object directly — no `x402ResourceServer` wrapper, but you assemble the facilitator client and scheme registration yourself: ```js import { paymentMiddlewareFromConfig } from "@x402/express"; import { HTTPFacilitatorClient } from "@x402/core/server"; import { ExactStellarScheme } from "@x402/stellar/exact/server"; import { Keypair, StrKey } from "@stellar/stellar-sdk"; const facilitator = new HTTPFacilitatorClient({ // @x402/core falls back to its own DEFAULT_FACILITATOR_URL // ("https://x402.org/facilitator") when this is unset. That facilitator
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub