> ## Documentation Index
> Fetch the complete documentation index at: https://docs-test.rye.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart with AgentCash

> Buy a product from any merchant using the AgentCash SDK and pay with USDC over x402.

<Card title="← x402" href="/api-v2/x402/overview" />

This walkthrough shows how to purchase a product through Rye's [x402](/api-v2/x402/overview) endpoint using the [AgentCash](https://agentcash.dev) client. AgentCash handles the `402 → sign → retry` loop transparently, so your code reads like a normal HTTP call.

## Prerequisites

* An AgentCash wallet with a small USDC balance on Base (≥ \$1 is plenty for testing — covers the access fees and a low-priced test purchase)
* A product URL from a supported merchant (Shopify storefronts, etc.)
* The AgentCash CLI or SDK — follow the [AgentCash setup guide](https://agentcash.dev/docs) to install and initialize a wallet

The first time you initialize AgentCash it generates a wallet and stores keys at `~/.agentcash/wallet.json` (EVM) and `~/.agentcash/solana-wallet.json` (Solana). Top up the wallet with USDC on the network you plan to use before making any paid call.

## End-to-end purchase

The example below uses [`@agentcash/fetch`](https://www.npmjs.com/package/@agentcash/fetch) — the programmatic AgentCash SDK. Its `executeFetch` function handles the `402 → sign → retry` loop transparently, so your code reads like a normal HTTP call.

```bash theme={null}
npm install @agentcash/fetch @agentcash/networks @solana/kit viem
```

```typescript theme={null}
import { privateKeyToAccount } from "viem/accounts";
import { generateKeyPairSigner } from "@solana/kit";
import { executeFetch, PaymentProtocol } from "@agentcash/fetch";
import { Network } from "@agentcash/networks";

const baseUrl = "https://x402.rye.com";

// AgentCash requires both wallets in the type even for an EVM-only flow.
const evm = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
const svm = await generateKeyPairSigner();

const params = {
  paymentProtocol: PaymentProtocol.X402,
  paymentNetwork: Network.BASE,
};

// 1. Create a checkout intent — pays $0.02 over x402 automatically.
const create = await executeFetch(
  {
    url: `${baseUrl}/v1/checkout-intents`,
    method: "POST",
    headers: {},
    body: JSON.stringify({
      productUrl: "https://shop.example.com/running-shoes",
      quantity: 1,
      buyer: {
        firstName: "Jane",
        lastName: "Doe",
        email: "jane@example.com",
        phone: "+14155551234",
        address1: "123 Market St",
        city: "San Francisco",
        province: "CA",
        country: "US",
        postalCode: "94103",
      },
    }),
  },
  { wallets: { evm, svm }, params, pickProtocol: async () => PaymentProtocol.X402 },
);
const created = await create.response.json();
const intentId = created.id;
// create.paymentInfo.payment.transactionHash is the on-chain settle tx

// 2. Poll for the offer — free, no signature required.
async function pollUntil(targetState: string) {
  while (true) {
    const r = await fetch(`${baseUrl}/v1/checkout-intents?id=${intentId}`, {
      headers: { "X-Wallet-Address": evm.address },
    });
    const intent = await r.json();
    if (intent.state === targetState || intent.state === "failed") return intent;
    await new Promise((res) => setTimeout(res, 3000));
  }
}

const ready = await pollUntil("awaiting_confirmation");
if (ready.state === "failed") throw new Error(ready.failureReason?.message);

// 3. Inspect the offer and decide whether to proceed.
const totalSubunits = ready.offer.cost.total.amountSubunits;
const budgetSubunits = 15000; // $150.00
if (totalSubunits > budgetSubunits) throw new Error("over budget");

// 4. Confirm — pays purchase total + $0.03 over x402 in a single signed authorization.
await executeFetch(
  {
    url: `${baseUrl}/v1/checkout-intents/confirm`,
    method: "POST",
    headers: {},
    body: JSON.stringify({
      id: intentId,
      paymentMethod: { type: "x402", network: "base" },
    }),
  },
  { wallets: { evm, svm }, params, pickProtocol: async () => PaymentProtocol.X402 },
);

// 5. Poll for completion.
const final = await pollUntil("completed");
console.log("order placed:", final.orderId);
```

<Tip>
  Prefer the CLI or an MCP-installed agent? AgentCash also ships `npx agentcash fetch <url>` and an MCP server (`claude mcp add agentcash --scope user -- npx -y agentcash@latest`). Both speak the same canonical x402 v2 against this endpoint.
</Tip>

## What happens behind the scenes

* `executeFetch` receives a `402 Payment Required` from the proxy with a `PAYMENT-REQUIRED` header. It signs an EIP-3009 `TransferWithAuthorization` (off-chain only — the buyer never broadcasts) and retries the same request with a `PAYMENT-SIGNATURE` header.
* The proxy verifies the signature, broadcasts `transferWithAuthorization` on Base, and pays the gas. On the success response it returns an `X-PAYMENT-RESPONSE` header carrying the on-chain transaction hash, which `executeFetch` exposes as `paymentInfo.payment.transactionHash`.
* For step 1 the authorization is for $0.02. For step 4 it covers the offer's purchase total plus the $0.03 API fee — bundled into a single signed authorization.
* `GET` calls in steps 2 and 5 are free and use a normal `fetch`. The `X-Wallet-Address` header scopes the read to the wallet that paid for the intent.
* After step 4 returns, Rye places the order asynchronously. The intent moves to `placing_order`, then to `completed` or `failed`. See [Checkout Intent Lifecycle](/api-v2/checkout-intent-lifecycle) for the full state machine.

## Timing

| Step                                  | Typical latency |
| ------------------------------------- | --------------- |
| Create intent (incl. on-chain settle) | 1–3 s           |
| Offer retrieval                       | 5–15 s          |
| Confirm (incl. on-chain settle)       | 1–3 s           |
| Order placement                       | 30–60 s typical |

Order placement runs asynchronously — your code does not block while Rye places the order at the merchant. Keep polling `GET /v1/checkout-intents?id=…` until you see `state: "completed"` or `state: "failed"`.

## Failure modes

* **Insufficient wallet balance** — the on-chain transfer fails; the intent moves to `failed` and no order is placed.
* **Offer retrieval fails** (out of stock, unsupported product) — `state: "failed"` after step 2; the \$0.02 access fee is not refunded because the offer retrieval work was performed.
* **Order placement fails** at the merchant — `state: "failed"` after step 4; the purchase amount is automatically refunded to the signing wallet on-chain.
* **Signature expired** between calls — the retry returns `400` with a fresh `PAYMENT-REQUIRED`; `executeFetch` re-signs and retries automatically.

See the [Endpoint Reference](/api-v2/x402/reference) for full request and response shapes, including how to call the API without the AgentCash SDK.
