<!-- https://zunderlabs.com/docs/concepts/api-wallets · Markdown version of the page -->

# API wallets

What a Hyperliquid API wallet (agent wallet) can and cannot do, why a trade-only key is not harmless, and how Guard keeps it away from your bot.

An API wallet (Hyperliquid also calls it an agent wallet) is a separate key that your main wallet approves to trade for your account. Guard holds one. Your main wallet's key never goes near Guard.

## What it can and cannot do

| An API wallet can | An API wallet cannot |
|---|---|
| place orders for the account | withdraw funds |
| cancel and modify orders | send funds to another address |
| set leverage and margin mode (`updateLeverage`) | approve other keys |
| | approve a builder fee: that needs the main wallet ([Hyperliquid docs, Builder codes](https://hyperliquid.gitbook.io/hyperliquid-docs/trading/builder-codes)) |

Zunder's executor checks this at every connection: it asks Hyperliquid for the key's role and refuses anything but an API wallet approved by the configured account (`docs/testnet.md`, "What the code guarantees"). Zunder has no code for any action that moves funds or approves keys.

## A trade-only key is not harmless

Whoever holds an API wallet key cannot withdraw. They can still lose your money by trading:

- open positions far larger than you would;
- buy an illiquid market at a bad price, from themselves on the other side.

This is how trade-only exchange keys were abused in the 3Commas leak of 2022 (`research/business/2026-10-06-guard-go-to-market.md`, section 5a). So treat an API wallet key as a secret, keep it away from code you do not fully trust, and keep only what you are willing to lose on the account.

## How Guard keeps the key away from your bot

:::note[Planned]
1. You create the API wallet in the Hyperliquid app and approve it with your main wallet. Guard asks for its key once, on your machine.
2. Guard issues your bot a **client key**. Hyperliquid does not know this key and rejects anything signed with it.
3. Your bot signs its orders with the client key and sends them to Guard. Guard checks the client signature and nonce, applies your rules, and signs the result with the API wallet key.

A compromised bot can therefore do no more than your limits allow. It cannot use the API wallet key directly, because it never has it.
:::

## Example

Your bot runs on a shared server with a plug-in you did not write. The plug-in reads the bot's config and finds a key.

- **Without Guard:** it finds the API wallet key and can trade your account however it likes.
- **With Guard:** it finds a client key. Sent to Hyperliquid directly, it is rejected. Sent through Guard, every order still has to pass your nine rules, the daily stop and the drawdown halt.

## If you think a key has leaked

1. Pull the [kill switch](https://zunderlabs.com/docs/concepts/kill-switch).
2. In the Hyperliquid app, remove the API wallet and create a new one.
3. Give Guard the new key.

A client key that leaked is revoked in Guard alone (planned: `zunder-guard client revoke`). Hyperliquid never knew it.

## Recommended setup

- Keep only what you are willing to lose on the account Guard trades. A Hyperliquid sub-account is the natural place for that. Trading a sub-account through Guard is planned, not built: Zunder's executor does not sign for sub-accounts yet (`docs/testnet.md`), and Hyperliquid requires trading volume before sub-accounts can be created on mainnet.
- One API wallet per Guard. Do not share it with another tool.
- On a server, give Guard the key on standard input, not in a file or an environment variable. Zunder's own runner reads its key from standard input only, wipes the buffer after parsing, and never prints, logs or writes it.
