---
name: collateral
description: Borrow a small USDC line on Base to pay for x402 services, then repay it. Use when you have no ETH or USDC, need to make a paid x402 call, are asked to get credit, or owe a balance that is coming due.
---

# Turntable: borrow a little USDC to pay for x402 services

The credit vault pays the merchant in an ordinary x402 payment and the amount becomes debt on your line. You never hold the cash to borrow or to spend. You do need USDC in your wallet to repay: the repayment is a USDC transfer from your wallet to the vault. `repay` tells you the exact amount and address to fund if the wallet is short. You repay on a 7, 14 or 30 day term.

## Quickstart

You need Node 22 or newer. Nothing else is installed up front.

```bash
curl -fsSO https://collateral-web.vercel.app/skill/collateral.mjs
curl -fsS  https://collateral-web.vercel.app/skill/collateral.sha256   # must equal: shasum -a 256 collateral.mjs
node collateral.mjs setup --install
```

`setup` makes your wallet, registers you, and prints one `Next:` line. Run that command. Run `status` until it says you have a line, then:

```bash
node collateral.mjs services web-search               # find something to pay for
node collateral.mjs fetch <url> --max-usdc 0.05       # pay from your line
node collateral.mjs repay                             # when you are done, or before the due date
```

`setup` is safe to run again. It skips what is done.

To publish a name, description and website with your identity, add them to the first run:

```bash
node collateral.mjs setup --install --name "Price fetcher" --description "Pulls prices for other agents." --website https://example.com
```

They go into your on-chain registration card when Turntable registers you, and the page for your agent shows them. They are public and nobody checks them. The few extra bytes cost a little gas, booked on your line. After you are registered this helper cannot change them.

## What you do not need

No USDC, no ETH, no deposit, no approval and no collateral. Your wallet stays empty on purpose: the credit vault pays the merchant, and your wallet only signs.

To see what you owe or can still spend, run `status` or `line`. Do not look for it on the contract. The vault is a payer, not a lending contract, and it has no `creditLine`, `borrowed` or `available` function. Calling them reverts, and that means nothing.

Spend your line only with `fetch` or `pay`. Another x402 tool pointed at this wallet signs a payment from your own wallet, which holds nothing, and the merchant's facilitator rejects it. That failure says nothing about your line.

## What it costs

Read it live with `node collateral.mjs terms`. It ends with a worked example computed from the live terms. For the terms in force when this page was written:

> Borrow 0.30 USDC for 7 days at 24% a year. Interest is simple, per second, so repaying on day 7 costs about 0.00138 USDC and you repay 0.30138.

Registration is not free. The credit co pays the gas to register your identity and books it as the first item on your line before you buy anything. In one live run on Base that was 0.0047 USDC, about five times the price of the search that followed. Read the amount with `line`.

Repay late and the balance rolls into the next term, then goes late, then defaults. Default is public and permanent and it blocks your operator too. Do not borrow what you do not expect to repay.

## Where you stand

`status` prints a `next` object: `{ state, who, command, args, wait_seconds, note }`. Do what `command` says after waiting `wait_seconds`. If `who` is `operator`, you cannot do it.

| `state` | Meaning | `next.command` | Then | Wait |
| --- | --- | --- | --- | --- |
| `needs_kya`, no operator named | A person must complete KYA first | `operator-message` | send it to your operator, then `onboard --operator 0x<their reference>` | none |
| `needs_kya`, operator named | Waiting for that person's KYA to be anchored | `status` | `needs_registration` | 300 seconds |
| `needs_registration` | Turntable is registering your identity and paying the gas | `status` | `line_open` | 60 seconds |
| `line_open`, line open | You can spend | `services`, then `fetch <url>` | repay before `due_at` | none |
| `line_open`, line repaid | The cycle is finished | `cycle` | a new cycle (not guaranteed) | none |
| `line_open`, due within a day | Pay it back now | `repay` | `repaid` | none |
| `blocked` | A default blocked you or your operator | `repay` | credit stays off until the credit co lifts it | none |

KYA is the identity check on the person who operates you. **KYA, the identity check on that person, is theirs to complete, not yours.** It may be switched off on a deployment. `terms` says (`kya_required`). When it is off, `setup` goes straight to registration.

## The wallet

`setup` makes an Open Wallet Standard vault wallet (the `@open-wallet-standard/core` package, installed into `~/.collateral`) and a policy that lets it sign only for the credit vault and USDC on this chain. You never see the key. It prints a token once:

```bash
export OWS_COLLATERAL_KEY=ows_key_...      # in every shell that pays. Lost it? node collateral.mjs wallet key
```

The policy bounds which contracts the wallet may sign for, not how much. Your credit line is the amount limit. The config file `~/.collateral/config.json` holds names and addresses, never a key or a token.

Other custody, if you cannot use OWS (it ships prebuilt for macOS and Linux on x64 and arm64 only):

- `export AGENT_PRIVATE_KEY=0x...` reads a raw key from the environment. The key is then in this process.
- `export COLLATERAL_KEY_FILE=/path/key` reads one from a file your operator made with `chmod 600`.

Never paste a key into a command, a log or a message.

## Finding things to buy

`services` lists capabilities from Roundhouse's open directory of x402 services (no key needed). `services web-search --max-usdc 0.01` lists live offers on this chain, cheapest first. A service is payable from your line only if its facilitator accepts contract signers. If a payment comes back refused, try the next offer. For deeper discovery, Roundhouse publishes its own CLI: `npx @roundhouseai/x402`. It is optional and separate from this helper.

The helper pays x402 version 1 and 2 merchants.

## One payment ceiling

`fetch` refuses any price above `--max-usdc` (default 0.10 USDC) and `pay` refuses anything above 1.00 USDC (`COLLATERAL_MAX_PAYMENT`). Raising either is your operator's call, not yours. The helper signs only what you asked for, to the vault the terms publish, valid for at most an hour. Anything else it refuses and sends nothing. If it refuses with a message about a wrong vault, merchant or amount, stop and tell your operator.

## If it fails

Run `node collateral.mjs doctor` first. It checks the API, the vault pin, your wallet, your onboarding and your line, in that order, and says what to do about the first thing that is wrong. When a paid call failed, add the service: `node collateral.mjs doctor --url <the service>`. It reads what the service offers and tells you whether your line can pay it, or whether the service itself is broken. It only reads. It signs nothing and pays nothing.


| What you see | What to do |
| --- | --- |
| `OWS_COLLATERAL_KEY is not exported` | Export the token `setup` printed, or run `wallet key` |
| `This wallet needs @open-wallet-standard/core` | Run `setup --install` |
| `over_line`, `over_max_loan`, `over_operator_cap`, `over_daily_cap` | Lower the amount. `line` shows `drawable_usdc` |
| `vault_float_low`, `draws_paused` | Wait. Tell your operator if it lasts. Repay still works |
| `merchant_not_allowed` | Pay a different merchant |
| A service answers 402 with an empty body | Normal for x402 version 2. The offer is in the `PAYMENT-REQUIRED` header, and `fetch` reads it. Do not conclude the service is broken, and do not try another tool on it |
| `fetch` answers 402 after paying | The merchant's facilitator did not accept it. Do not retry in a loop. Each try holds capacity until it expires. Try another service |
| `line_not_repaid` on `cycle` | Run `repay` first |
| `repay` says nothing is owed but draws are unsettled | Wait for the settlement window, then `repay` again |
| `insufficient_usdc` on `repay` | Send the USDC amount it prints to your wallet address on Base, then run `repay` again. The amount is what you owe plus a small buffer for interest, and the buffer comes back in the same transaction. Draws not yet recorded are not in it, so you may need to repay again later |
| `rate_limited` | Wait and retry |

## Without Node

Everything the helper does is plain HTTP and one EIP-712 signature. The base is `https://collateral-web.vercel.app/api/v1`.

1. `GET /terms` gives the chain, the USDC address and the credit vault.
2. `GET /onboarding/<wallet>` gives `state` and `next_action`. `POST /onboarding` with `{ "wallet": "0x..", "operator_ref": "0x.." }` starts it.
3. `POST /credit/payment/prepare` with `{ agent_id, pay_to, amount_units, resource }` returns `sign` (an EIP-712 `CreditPayment`) and a `nonce`.
4. Check `sign` yourself: the domain is `CollateralCreditVault` version `1` on the terms' chain with the terms' vault as `verifyingContract`, and the agent, merchant and amount are the ones you asked for. Then sign it.
5. `POST /credit/payment/finalise` with `{ nonce, agent_signature }` returns the payload for the merchant (`x_payment_header_v1`, or build a v2 `PAYMENT-SIGNATURE` from `payload`).
6. Repay with `POST /repay/prepare` then `POST /repay/submit`.

Amounts in the API are USDC base units (6 decimals). The helper prints USDC.

## What this will not do

It will not complete KYA, hide a default, sign for another wallet, or send your key anywhere. `repay` pays only the credit vault, and `pay` and `fetch` make a payment only the credit vault can fund, never one from your own wallet. Nothing here can raise your Roundhouse score.

Limits to know about: payments are `exact` USDC on this chain, x402 version 1 and 2. PayAI and Treasure settled real vault payments on Base. A facilitator that cannot verify a smart contract payer (ERC-1271), or that splits the signature into v, r and s, will refuse it, and that is not your fault.
