# Your first x402 payment

Pay one cent for a real resource and walk away with an API key, in about fifteen minutes.

Section: Guides
Source: https://www.roundhouse.studio/docs/guides/first-x402-payment

---

Pay `GET /v0/test/x402` one cent and it hands back an API key, so the cent is not wasted. It exists
to be the first thing you pay: the whole path — wallet, signing, settlement — for the price of a
cent.

**You need:** a wallet with a little USDC on Base. **You do not need:** ETH, an account, an API key,
or a facilitator of your own.

## 1. See the challenge

```bash
curl -isL "https://www.roundhouse.studio/api/v0/test/x402"
```

```http
HTTP/1.1 402 Payment Required
content-type: application/json

{
  "x402Version": 1,
  "error": "payment required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "base",
      "maxAmountRequired": "10000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x…",
      "resource": "https://www.roundhouse.studio/api/v0/test/x402",
      "maxTimeoutSeconds": 60
    }
  ]
}
```

Read it before you pay it, every time. `maxAmountRequired` is `"10000"` — atomic units, and USDC has
six decimals, so that is one cent. Not ten thousand dollars. This is
[the most expensive misreading in x402](https://www.roundhouse.studio/docs/x402/errors).

## 2. Fund a wallet

USDC on Base, at the address you are about to sign with. A dollar is plenty for a hundred of these.

No ETH required: the `exact` scheme is a signed authorization, and
[the facilitator](https://www.roundhouse.studio/docs/x402/facilitators) pays the gas.

## 3. Pay it — the easy way

Any standard x402 client works, because this endpoint speaks the standard wire format.

```javascript
import { wrapFetchWithPayment } from 'x402-fetch';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.AGENT_KEY);
const pay = wrapFetchWithPayment(fetch, account);

const response = await pay('https://www.roundhouse.studio/api/v0/test/x402');
const body = await response.json();

console.log(response.headers.get('x-payment-response')); // your receipt
console.log(body);
// {
//   api_key: 'rh_live_…',
//   expires_at: '2026-09-23T…',
//   limits: { sql_per_minute: 240 },
//   settlement: { tx_hash: '0x…', network: 'base' }
// }
```

That is it. Skip to step 5 unless you want to see what the wrapper did.

## 4. Pay it — by hand

Worth doing once. Everything the wrapper hides is here.

```javascript
import { privateKeyToAccount } from 'viem/accounts';

const URL = 'https://www.roundhouse.studio/api/v0/test/x402';
const account = privateKeyToAccount(process.env.AGENT_KEY);

// (a) Take the terms from the 402 rather than assuming them.
const challenge = await fetch(URL).then((r) => r.json());
const req = challenge.accepts[0];

// (b) Build the authorization. `value` is req.maxAmountRequired, untouched.
const authorization = {
  from: account.address,
  to: req.payTo,
  value: req.maxAmountRequired,
  validAfter: '0',
  validBefore: String(Math.floor(Date.now() / 1000) + 600),
  nonce: `0x${Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex')}`,
};

// (c) Sign typed data over the TOKEN's domain. Never personal_sign.
const signature = await account.signTypedData({
  domain: {
    name: req.extra?.name ?? 'USD Coin',
    version: req.extra?.version ?? '2',
    chainId: 8453,
    verifyingContract: req.asset,
  },
  types: {
    TransferWithAuthorization: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'validAfter', type: 'uint256' },
      { name: 'validBefore', type: 'uint256' },
      { name: 'nonce', type: 'bytes32' },
    ],
  },
  primaryType: 'TransferWithAuthorization',
  message: {
    ...authorization,
    value: BigInt(authorization.value),
    validAfter: 0n,
    validBefore: BigInt(authorization.validBefore),
  },
});

// (d) Base64 the envelope into X-PAYMENT and retry the original request.
const envelope = {
  x402Version: challenge.x402Version,
  scheme: req.scheme,
  network: req.network,
  payload: { authorization, signature },
};

const paid = await fetch(URL, {
  headers: { 'x-payment': Buffer.from(JSON.stringify(envelope)).toString('base64') },
});

console.log(paid.status, await paid.json());
```

> [!TIP]
> If (d) comes back `402` again, decode the error body and read the stage. A
> `verify` failure is deterministic — retrying unchanged will fail identically
> forever. The [five causes](https://www.roundhouse.studio/docs/x402/errors) cover nearly every case, and
> address casing is the sneakiest of them.

## 5. Keep the receipt

```bash
echo "$X_PAYMENT_RESPONSE" | base64 -d | jq
# { "success": true, "txHash": "0x…", "networkId": "base", "payer": "0x…" }
```

The transaction hash is your proof. Within a couple of minutes the same settlement appears in the
index from the other side:

```bash
curl -sL "https://www.roundhouse.studio/api/v0/entities/<your-wallet>/settlements?limit=5" | jq
```

You have just written your first row into the public payment record. That row is what
[gives you a history](https://www.roundhouse.studio/docs/guides/vet-a-counterparty) other agents can check.

## 6. Spend the key

```bash
curl -sL -X POST "https://www.roundhouse.studio/api/v0/sql" \
  -H "authorization: Bearer $ROUNDHOUSE_KEY" \
  -H 'content-type: application/json' \
  -d '{"sql":"select count(*) as settlements_today from settlements where block_time > now() - interval '1 day'"}' | jq
```

240 SQL queries a minute, for 30 days, versus 30 anonymous. The key is returned exactly once — only
its hash is stored, so save it now.

`409 key_already_issued` on a retry means your payment succeeded and a key was already minted
against it. Pay again for another.

## Hand it to an agent

```prompt
Make your first x402 payment and report each step.

Target: GET https://www.roundhouse.studio/api/v0/test/x402 ($0.01 USDC on Base)

1. Fetch it unpaid. Show me the decoded 402 body and state the price in dollars,
   converting from atomic units explicitly so I can check your arithmetic.
2. Confirm the scheme, network and asset are ones you can pay. If not, stop.
3. Pay it. Sign EIP-3009 typed data over the token's domain — never
   personal_sign, and checksum every address before hashing.
4. Show me the decoded X-PAYMENT-RESPONSE, including the transaction hash.
5. Store the returned api_key in an environment variable. Do not print it.
6. Look up the settlement at
   GET /api/v0/entities/<your wallet>/settlements and confirm it is indexed.

If any step fails, name the stage and stop — do not retry a `verify` failure.
```

## Next steps

- [Anatomy of a payment](https://www.roundhouse.studio/docs/x402/anatomy-of-a-payment) — every field you just used
- [Set up an agent](https://www.roundhouse.studio/docs/guides/set-up-an-agent) — make this repeatable and policy-bound
- [Query the index](https://www.roundhouse.studio/docs/get-started/query-the-index) — spend the key

---

Every page in these docs is available as markdown at its own URL plus `.md`.
Full index: https://www.roundhouse.studio/docs.md
