# Wallets and agent keys

> Each agent gets a wallet and an ed25519 keypair. The wallet holds the public key and the agent holds the private key. How to create both, and how to look after them.

## One wallet per agent

A wallet holds money and belongs to your workspace. An **agent wallet** also holds the **public key** that its payment proofs are checked against. Give each agent its own wallet:

- a compromised or misbehaving agent can only spend what its own wallet holds;
- purchase history and receipts stay separate per agent;
- the MCP packages can find the wallet from your key automatically, but only when the workspace has exactly one agent wallet. With several, you name the one to use.

## Create one in the console

**Buyer → Agents → New agent.** The console generates the keypair, creates the wallet, and shows the private key **once** as a download. It also keeps a copy in that browser, so the console can make test calls.

## Create one with the API

### Step 1: Generate a keypair

```bash title="OpenSSL"
openssl genpkey -algorithm ed25519 -out agent-key.pem
chmod 600 agent-key.pem
openssl pkey -in agent-key.pem -pubout          # the public half, for the next step
```

```ts title="TypeScript"
import { generateEd25519KeyPair } from "@zanora/sdk";

const { publicKeyPem, privateKeyPem } = generateEd25519KeyPair();
// Store privateKeyPem somewhere only the agent can read.
```

```bash title="Node, no packages"
node -e 'const {generateKeyPairSync}=require("node:crypto");
const {publicKey,privateKey}=generateKeyPairSync("ed25519");
require("node:fs").writeFileSync("agent-key.pem",privateKey.export({type:"pkcs8",format:"pem"}),{mode:0o600});
console.log(publicKey.export({type:"spki",format:"pem"}))'
```

### Step 2: Register the wallet

```bash
curl -s -X POST https://api.zanora.dev/v1/wallets -H "x-api-key: $WKEY" \
  -H 'content-type: application/json' \
  -d '{"ownerType":"agent","ownerId":"invoice-bot","publicKeyPem":"-----BEGIN PUBLIC KEY-----\n…\n-----END PUBLIC KEY-----\n"}'
```

`ownerId` is your label for the agent. It appears in payment proofs, so use something plain like `invoice-bot`. The response is the wallet, including its `id` (`wal_…`).

## Looking after the private key

> **Danger — It's the spending authority, and it can't be recovered:**
>
> Whoever has `agent-key.pem` can spend that wallet's balance, within your policies. Zanora never had the private key, so a lost key can't be reissued. The wallet keeps its balance but can never spend it. Treat the key like a wallet seed phrase.

- Keep it in a file with mode `0600`, or in your secrets manager. In MCP configs, point to it with `ZANORA_AGENT_KEY_FILE` rather than pasting it in, because config files get shared and appear in screen-shares.
- Only fund a wallet with what you'd accept losing to that agent.
- If a key leaks: **freeze** the wallet (`POST /v1/wallets/:id/freeze`), create a new wallet with a new key, and move future funding there.

## Reading a wallet

| Call | Returns |
|---|---|
| `GET /v1/wallets` | every wallet in your workspace |
| `GET /v1/wallets/:id/balance` | `{ balanceMinor, availableMinor, currency }`. Available excludes money on hold |
| `GET /v1/wallets/:id/ledger` | every entry, oldest first: funding, charges, refunds |
| `POST /v1/wallets/:id/freeze` / `unfreeze` | stop or allow outgoing payments |

Next: [fund the wallet](https://docs.zanora.dev/buyers/funding.md).
