Skip to main content
You have an AI agent — Claude Code, Cursor, or anything that can run a Node.js script. This page gets that agent from zero to its first paid, useful result from Otto AI using nothing but a wallet with a little USDC on Base. There is no signup and no API key. Otto’s services speak x402, a pay-per-request standard: your agent requests an endpoint, receives 402 Payment Required, signs a tiny USDC payment locally, retries, and gets the data. Every command on this page was run live against the production catalog before it was published.

What you need

  1. Node.js 20 or later. This guide was verified end-to-end on v22.
  2. A wallet with a few dollars of USDC on Base. Cheap intelligence reads cost $0.001 each, so even $1 goes a long way. Buy USDC on any exchange and withdraw it on the Base network (native USDC, contract 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913).
  3. That wallet’s private key, exported as an environment variable. It signs payment authorizations locally — it is never sent to Otto.
Use a fresh, dedicated wallet that holds only what you plan to spend — never your main wallet’s key. Any key you hand to agent tooling should be treated as disposable.
No ETH needed. x402 payments on Base are gasless USDC authorizations (EIP-3009) — the payment facilitator submits the on-chain transaction and pays the gas. Your wallet only needs USDC. Polygon and Solana USDC are also accepted; this guide pins Base as the simplest path.

Setup

Copy-paste, in a terminal:
mkdir otto-agent && cd otto-agent
npm init -y
npm install @x402/axios @x402/evm axios viem
Give the session your payer key (0x + 64 hex characters — never commit it anywhere):
export X402_PRIVATE_KEY=<YOUR_PRIVATE_KEY>
Create otto.mjs:
otto.mjs
// otto.mjs — first paid call to Otto AI over x402
import axios from "axios";
import { wrapAxiosWithPayment, x402Client, decodePaymentResponseHeader } from "@x402/axios";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { toClientEvmSigner } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";

// Your wallet. The key stays on your machine — it signs a USDC transfer
// authorization locally; it is never sent to Otto.
const account = privateKeyToAccount(process.env.X402_PRIVATE_KEY);
const publicClient = createPublicClient({ chain: base, transport: http() });

const client = new x402Client();
client.register("eip155:8453", new ExactEvmScheme(toClientEvmSigner(account, publicClient)));

const otto = wrapAxiosWithPayment(
  axios.create({ baseURL: "https://x402.ottoai.services", timeout: 60000 }),
  client,
);

// $0.001 USDC — the client sees the 402, signs the payment, retries, returns data.
const res = await otto.get("/crypto-news");
console.log(res.data);

const receipt = decodePaymentResponseHeader(
  res.headers["payment-response"] ?? res.headers["x-payment-response"],
);
console.log("\nSettled on-chain:", receipt?.transaction);

First paid call

node otto.mjs
That’s it. Real output from a live run of exactly this script (abridged):
{
  status: 'success',
  data: {
    report: '## MARKET BRIEF\n' +
      '\n' +
      'Your Crypto News Brief\n' +
      '\n' +
      'Overview: The cryptocurrency market is currently characterized by robust
       stablecoin utility and increasing institutional interest, ...\n' +
      'Market Sentiment:\n' +
      'Overall Bull/Bear Score: 55\n' +
      ...
  }
}

Settled on-chain: 0x9853ee5619ee060318580f7adfbe685ab0a67f9c0f21c2a5c8e707061b84d2fe
What just happened:
  1. Your request hit the paywall and got a 402 describing the price ($0.001 USDC) and where to pay.
  2. The client signed a USDC transfer authorization with your key — locally.
  3. It retried the request with the signed payment attached.
  4. Otto’s facilitator settled it on Base and returned the data plus a settlement transaction hash you can check yourself on Basescan.
Endpoints take query parameters the way you’d expect. Swap the request line for a token snapshot (also $0.001, also verified live):
const res = await otto.get("/token-details?symbol=ETH");

What else your wallet now buys

That one funded wallet is now a key to Otto’s whole x402 catalog — market and token intelligence, KOL sentiment, token security scans, yield discovery, onchain analytics, web and domain intelligence, real-world data (FX, weather), AI research, image and video generation, and trade-execution services. The live endpoint list with current prices is always at:
Using Claude Code or Cursor? Paste the setup above once, then tell your agent to fetch llm.txt and use otto.mjs as its template for calling any endpoint in the catalog. Not sure which endpoint fits a task? The x402 Open Router (POST /meta-intelligence, $0.001) recommends the right one.

Troubleshooting

The three failures new users actually hit — each reproduced live while writing this page:
SymptomCauseFix
Response is {}, status 402 after payment was attempted (or decodePaymentResponseHeader throws “not correctly encoded”)The wallet has no USDC on Base — the settlement reverted. The retry’s payment-required header decodes to invalid_payload: contract call failed: … execution reverted.Send USDC (native Base USDC, contract above) to the payer wallet. No ETH needed.
Cannot read properties of undefined (reading 'slice') on startupX402_PRIVATE_KEY isn’t set in this shell — export it again (it doesn’t survive a new terminal).export X402_PRIVATE_KEY=<YOUR_PRIVATE_KEY> in the same shell you run node from.
invalid private key, expected hex or 32 bytesThe key is malformed — it must be 0x followed by exactly 64 hex characters.Re-copy the key from your wallet’s export screen; check for a missing 0x prefix or truncation.
Two smaller gotchas: save the script as .mjs (plain .js without "type": "module" in package.json breaks the import statements), and if a request sits until the timeout, just re-run it — a payment signature is only valid for a short window, so a stalled attempt should be retried fresh, never resumed. Still stuck? Getting help lists the support channels.

Custody, and what’s coming

On this page, you hold the wallet: your key signs every payment locally, Otto never sees it, and every call is a discrete micro-payment you can audit on-chain. This bring-your-own-wallet rail is live today. Separately, Otto’s dApp accounts are agent-custodial today (an Otto-managed key signs); non-custodial Safe7579 accounts are in gated early access. A “fund one Safe once and Otto auto-pays your agent’s calls under a capped, revocable permission” rail is part of that same gated early access — it is not live, and this page will link the exact steps when it is. Until then, the path above is the way in. Details: Empire Access Layer.