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

# Build an x402-paying agent

> Wire an LLM agent to pay for HTTP 402-gated resources using Tally for the wallet + permission layer.

This guide walks through an LLM agent that autonomously pays for a
paywalled API call using the [x402][x402] protocol shape — with
Tally providing the wallet + permission layer.

By the end you'll have a working agent that:

* Receives a natural-language task ("what's the weather in Tokyo?")
* Decides on its own to call a paid tool
* Handles an HTTP `402 Payment Required` response by paying with Tally
* Verifies the payment landed on-chain before being served the data
* Returns the answer to the user

Either Claude or GPT works — the example auto-detects which API key
you've configured.

The runnable source lives at
[github.com/pkohler95/tally-examples](https://github.com/pkohler95/tally-examples)
in the `paying-weather-agent/` directory — clone it to follow along,
or read on for the inline walkthrough.

[x402]: https://www.x402.org

## What you'll build (and why)

Most "agent" demos today either fake the payment side (no real money
moves) or use a bespoke integration (only works with one vendor).
This example uses two pieces of real infrastructure:

* **Tally** for the wallet + permission layer. The agent is granted
  scoped spending authority (per-tx max, daily cap, optional
  recipient allowlists) on a wallet, and Tally enforces it server-
  side. No keys on the agent's box.
* **x402 protocol shape** for how the receiver communicates "you owe
  me \$X." Coinbase's x402 standard is the closest thing to a
  protocol for agentic payments. The same flow works with any
  service that follows it.

Together they answer "how does an agent autonomously pay for things
on the internet?" — with real wallets, real on-chain payments, and
real policy enforcement, while you're still on testnet so nothing
catastrophic happens if the agent makes a mistake.

## Prerequisites

### In the Tally dashboard

This setup is identical to the [Quickstart](/quickstart); the
short version is:

1. **Sign in** at [app.tallyforagents.com](https://app.tallyforagents.com).
2. **Fund your Main Wallet** with Base Sepolia USDC + ETH.
   \~\$1 USDC + \~0.01 ETH is plenty:
   * USDC: [Circle's faucet](https://faucet.circle.com) → Base
     Sepolia → paste your wallet address
   * ETH: [Alchemy's faucet](https://www.alchemy.com/faucets/base-sepolia)
3. **Register an agent** named `weather-agent` (Dashboard → Agents
   → Register agent). `weather-agent` is the example's default; if
   you pick a different name you'll set `TALLY_AGENT_ID` in
   `.env.local` later.
4. **Grant a permission** to that agent on your Main Wallet. Defaults
   ($10/tx, $100/day) are more than enough — the mock weather
   service charges \$0.05 per query.
5. **Create an API key** (Dashboard → API keys → Create). Copy the
   plaintext (shown once).

### For the LLM side

One of:

* **Anthropic API key**
* **OpenAI API key**

The example auto-detects which you have. Anthropic wins ties; unset
`ANTHROPIC_API_KEY` for a run to force OpenAI.

### For the code

Clone the example repo OR install the deps in your own project:

```bash theme={null}
# Clone and install (recommended):
git clone https://github.com/pkohler95/tally-examples.git
cd tally-examples && pnpm install

# Or add the deps to your own project:
npm install @tallyforagents/sdk viem dotenv
# plus ONE of:
npm install @anthropic-ai/sdk
# OR
npm install openai
```

## The agent + mock server

The runnable example splits across four files:

* `server.ts` — reference x402 weather service (HTTP server with
  on-chain verification). **The agent doesn't need this to run** —
  by default it talks to Tally's hosted demo endpoint at
  `https://app.tallyforagents.com/api/demo/x402-weather`. The local
  server is here as a reference implementation you can read, modify,
  or self-host if you want.
* `tools.ts` — the `get_weather` tool definition + payment-and-retry
  logic
* `agent-claude.ts` / `agent-openai.ts` — provider-specific agent
  loops (pick whichever matches your API key)
* `index.ts` — entry point that auto-detects which model SDK is
  available and dispatches

Each section below walks through one piece. If you only care about
**consuming** an x402 service (the agent side), you can skip the
server section.

## What's actually happening

The agent has access to one tool, `get_weather`. The LLM's system
prompt is implicit — Claude / GPT both recognize that paywalled
APIs are a real thing — but the tool's description spells it out:

```ts theme={null}
const TOOL_DEFINITION = {
  name: "get_weather",
  description:
    "Get the current weather for a city. This tool is paywalled: " +
    "the first call returns 402 Payment Required with an amount; the " +
    "tool automatically pays via the user's Tally wallet and retries.",
  parameters: {
    type: "object",
    properties: {
      city: { type: "string", description: "The city name" },
    },
    required: ["city"],
  },
};
```

When the LLM calls it, the implementation is one call to [`tally.x402.fetch()`](/sdk/x402) — the SDK does the 402 dance for you (initial fetch → parse terms → pay via Tally → retry with `X-Payment`):

```ts theme={null}
const { response, payment } = await tally.x402.fetch(
  `${serviceUrl}/weather?city=${city}`,
  {
    agent_id: agentId,
    wallet: walletAddress,
    idempotency_key: `weather-${city}-${Date.now()}`,
  },
);

if (response.ok) {
  const data = await response.json();
  if (payment) console.log(`paid ${payment.amount_usdc} USDC — ${payment.tx_hash}`);
  return data;
}
```

The SDK enforces a client-side cap (`max_amount_usdc`) if you pass one; the agent's [permission policy](/permissions) is the authoritative server-side bound. If the LLM somehow induced a too-large amount, Tally would reject before any on-chain action.

## The server side (mock x402)

The included `server.ts` is a \~150-line Node HTTP server that
follows the x402 response shape:

```ts theme={null}
// On any /weather request without X-Payment, return 402 with
// payment terms in the body.
{
  x402Version: 1,
  error: "X-Payment header required",
  accepts: [{
    scheme: "exact",
    network: "base-sepolia",
    maxAmountRequired: "50000",      // 0.05 USDC atomic units
    resource: "/weather?city=Tokyo",
    description: "Premium weather data",
    payTo: "0x...",
    asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC
  }]
}
```

When the retry comes in with `X-Payment: <tx-hash>`, the server uses
[viem](https://viem.sh) to verify on-chain:

```ts theme={null}
const receipt = await client.waitForTransactionReceipt({
  hash: txHash,
  timeout: 30_000,
});

// Walk the receipt logs for a USDC Transfer event with the right
// recipient and at-least amount.
for (const log of receipt.logs) {
  if (log.address.toLowerCase() === USDC_BASE_SEPOLIA.toLowerCase()) {
    const decoded = decodeEventLog({ abi: TRANSFER_ABI, ...log });
    if (decoded.args.to.toLowerCase() === expectedTo &&
        decoded.args.value >= expectedAmount) {
      return { ok: true };
    }
  }
}
```

This is real verification. The mock server doesn't trust the agent's
claim that "I paid you" — it checks Base Sepolia for the matching
on-chain transfer. Same model as production x402 services.

## What this teaches

Three concepts compound in the example:

1. **The LLM doesn't see money.** Tools take amounts as decimal
   strings; the LLM sees "this costs 0.05 USDC" but never touches
   keys or moves funds directly. Your code is the only thing that
   can call Tally — and Tally's policy is the only thing that can
   move funds.
2. **The x402 protocol is just HTTP.** No special SDK on the
   receiver side, no chain RPC from the agent's process, no Web3
   wallet to integrate. The agent makes a normal `fetch()` and
   reacts to the 402 response.
3. **Multi-layered enforcement.** Even if the LLM hallucinated a
   payment 100x bigger than expected, Tally would reject it
   (per-tx cap exceeded). Even if the agent's auth key were
   compromised, the most damage possible is bounded by the cap.

## Pointing at a real x402 service

The example's `WEATHER_SERVICE_URL` defaults to `http://localhost:4242`
(the mock server). When you want to talk to a real x402 service:

1. Find one that accepts Base Sepolia (most are mainnet-only today,
   but the ecosystem is growing).
2. Set `WEATHER_SERVICE_URL` to its URL.
3. Update the agent's permission to allow that service's wallet as
   a recipient (or leave the allowlist empty to allow any).

The code on the agent side is unchanged.

## Limitations of the demo

The mock server intentionally cuts corners that a real x402
implementation needs:

* **Bare tx hash** in `X-Payment` (real x402 uses a base64-encoded
  full payment payload)
* **No replay protection** — the same tx hash works repeatedly
* **No request-window check** — the tx could be hours old
* **Single payment scheme** — real x402 supports multiple

The agent-side code is closer to spec-compliant; the simplifications
are all server-side. The example README has the full list.

## Where to go next

* Read [Build a paying agent](/guides/build-a-paying-agent) for the
  more generic "agent + tool use + Tally" pattern (no x402)
* See the [SDK reference](/sdk/installation) for full
  `tally.payments.create()` options
* Check the [Permissions concept](/permissions) doc for how to set
  up tighter caps (e.g., a recipient allowlist that pins the
  weather service's wallet)
* File issues or improvements at
  [github.com/pkohler95/tally-v2](https://github.com/pkohler95/tally-v2)
