Start hereLive

Quickstart: agents

Give an agent its own Solana wallet and a fetch that pays 402s by itself, with a cap per call and a daily budget checked before every signature.

On this page+

1. Give the agent a wallet

Never run an agent on your main wallet. In the dApp, open Build & control → Agent wallets → New agent wallet. The keypair is generated in your browser and stored encrypted under a password you choose; download the encrypted backup it offers. Fund it from your main wallet with a few dollars of USDC, then use Reveal (password required) to get its secret key as base58 for the machine that runs the agent.

The agent needs no SOL to pay: in x402 the facilitator is the fee payer. When you want the money back, Sweep back returns everything in one transaction, with your main wallet paying the fee.

2. Install

Shell
npm install @x402/core @x402/fetch @x402/svm @solana/kit
npm pkg set type=module   # the example uses top-level await

3. Wrap fetch

agent.ts
import { createKeyPairSignerFromBytes, getBase58Encoder } from "@solana/kit";
import { x402Client, wrapFetchWithPayment, decodePaymentResponseHeader } from "@x402/fetch";
import { ExactSvmScheme } from "@x402/svm/exact/client";

// 64-byte secret key of the agent's own wallet, base58 (never your main wallet).
const signer = await createKeyPairSignerFromBytes(
  getBase58Encoder().encode(process.env.AGENT_SECRET_KEY!),
);

const DAILY_BUDGET = 2_000_000n; // 2.00 USDC in atomic units (6 decimals)
let spentToday = 0n;

const client = new x402Client()
  .register("solana:*", new ExactSvmScheme(signer, { rpcUrl: process.env.SOLANA_RPC_URL }))
  // per-call cap: anything above $0.05 is refused before signing
  .setSpendControls({ maxAmountPerPayment: "$0.05" })
  // daily budget: checked before every signature
  .onBeforePaymentCreation(async ({ selectedRequirements }) => {
    const amount = BigInt(selectedRequirements.amount);
    if (spentToday + amount > DAILY_BUDGET) {
      return { abort: true, reason: "daily budget reached" };
    }
  })
  // count it only once the seller has settled on-chain
  .onPaymentResponse(async ({ settleResponse, requirements }) => {
    if (settleResponse?.success) spentToday += BigInt(requirements.amount);
  });

const paidFetch = wrapFetchWithPayment(fetch, client);

// Axonpay's own xStocks table: $0.002 in USDC on Solana mainnet
const res = await paidFetch("https://axonpay.fun/api/x402/xstocks");
console.log(await res.json());

const receipt = res.headers.get("PAYMENT-RESPONSE");
if (receipt) {
  const { transaction, network, payer } = decodePaymentResponseHeader(receipt);
  console.log(`paid by ${payer} on ${network}: https://solscan.io/tx/${transaction}`);
}

wrapFetchWithPayment behaves like fetch until it meets a 402. Then it reads the offers, runs your guards, signs one transfer, retries the request once and hands you the paid response. The URL above is one of Axonpay’s own paid endpoints, so this runs for real on mainnet for $0.002. To try the 402 handling without paying anything, point it at https://axonpay.fun/api/x402/demo, which answers a genuine 402 and never settles.

What the guards do

GuardWhereBehaviour
Per-call capsetSpendControlsOffers above $0.05 are dropped before one is chosen. If you set nothing, the client still caps each payment at $1.
Daily budgetonBeforePaymentCreationRefuses to sign once today’s total would pass 2 USDC. The call throws Payment creation aborted: daily budget reached.
Settled-only accountingonPaymentResponseSpend is counted only when the seller returns a successful settlement.

This version keeps state in memory and can be raced by parallel calls. For an allowlist of endpoints and a budget that reserves before signing, use agent-fetch.ts from Mandates.

4. Run it

Shell
AGENT_SECRET_KEY=<base58 secret> SOLANA_RPC_URL=<your RPC URL> npx tsx agent.ts

Use a private RPC endpoint in production; the public one rate-limits quickly. Each paid call prints a Solscan link to its receipt. Testing against the devnet seller from Quickstart: sellers? Fund the agent with devnet USDC and point the URL at it; the solana:* registration already covers devnet.