---
title: "How to manage accounts and wallets"
description: "Create accounts, derive or import wallets, tag them, and unlock the ones with their own password."
---

> Documentation Index
> Fetch the complete documentation index at: https://tee.hypetrade.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# How to manage accounts and wallets

Everything here uses a workspace token. Creating and modifying needs the
`write` scope; listing needs `read`.

## Create an account

```bash
curl -X POST "$API_URL/accounts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"displayName":"Treasury","kind":"HD"}'
```

| Field | Required | Notes |
|---|---|---|
| `displayName` | yes | Human label; does not have to be unique. Must normalize to 4–64 characters and contain a Latin letter — the slug is derived from it |
| `kind` | no | `HD` (default) or `PK` |
| `secret` | on PK | Mnemonic for HD, private key for PK. Omit on HD to generate one; required on PK |
| `defaultNetwork` | no | Network slug this account derives against |
| `hasOwnPassword` | no | Give the account its own lock, separate from the session |
| `accountPassword` | when `hasOwnPassword` | The account's own password. Held to the same policy as a workspace password — at least 12 characters |

The response carries the account and whether a secret was generated:

```json
{
  "account": {
"slug": "treasury-a1b2",
"displayName": "Treasury",
"kind": "HD",
"hasOwnPassword": false,
"locked": false,
"defaultNetwork": "ethereum",
"wallets": 0
  },
  "generatedSecret": true
}
```

> **Read the slug from the response**
>
> The account slug is **generated by the service**, not chosen by you. It is the
> identifier every later call uses, so capture it. And note what is absent: a
> generated mnemonic is never returned. `generatedSecret: true` says one was
> created — that is all you get outside the sealed
> [export path](/guides/export-key-material/).

## Derive wallets

A new HD account has none. Derive in bulk:

```bash
curl -X POST "$API_URL/accounts/$ACCOUNT/wallets" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"count":5}'
```

```json
{ "before": 0, "after": 5 }
```

`count` is a positive integer up to 500 per call. The tenant-wide
`maxWallets` limit still applies — crossing it returns `409` with
`quota_wallets_exceeded`. Because the response reports both sides, a retry after
an ambiguous network failure is easy to reconcile.

## Import a private key

Import needs a **PK** account. The `$ACCOUNT` created above is HD, and an HD
account refuses an import with `422` and `unsupported_for_kind` — the two kinds
are exclusive in both directions, so a PK account equally refuses `derive`.
Create one for the key:

```bash
curl -X POST "$API_URL/accounts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"displayName":"Imported Key","kind":"PK","secret":"0x…"}'
```

`secret` is required on a PK account — there is nothing for the service to
generate. Then import further keys into it:

```bash
curl -X POST "$API_URL/accounts/$PK_ACCOUNT/wallets/import" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"privateKey":"0x…"}'
```

Adds exactly one wallet and returns it. A key the service cannot parse returns
`400` with `invalid_private_key`.

## List wallets and addresses

```bash
curl "$API_URL/accounts/$ACCOUNT/wallets" -H "Authorization: Bearer $TOKEN"
```

Each wallet carries its addresses inline:

```json
{
  "wallets": [
{
  "id": 1,
  "tags": ["hot"],
  "addresses": [
    { "publicKey": "0xabc…", "vm": "evm", "network": "ethereum", "chainId": 1 }
  ]
}
  ]
}
```

For one wallet's addresses only:

```bash
curl "$API_URL/accounts/$ACCOUNT/wallets/1/addresses" \
  -H "Authorization: Bearer $TOKEN"
```

## Tag wallets

Tags are yours to use; the service attaches no meaning to them. The call
**replaces** the whole list rather than appending:

```bash
curl -X PUT "$API_URL/accounts/$ACCOUNT/wallets/1/tags" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"tags":["hot","treasury"]}'
```

Up to 32 tags. To remove them all, send `{"tags":[]}`.

## Accounts with their own password

An account created with `hasOwnPassword: true` stays locked even while the
workspace is open, and answers `423` with `account_locked` until unlocked:

```bash
curl -X POST "$API_URL/accounts/$ACCOUNT/unlock" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"accountPassword":"…"}'
```

Returns `204`. Unlocks last for their own (usually short) window, and failed
attempts are rate-limited — `429` with `account_unlock_rate_limited`.

Lock it again as soon as you are done:

```bash
curl -X POST "$API_URL/accounts/$ACCOUNT/lock" -H "Authorization: Bearer $TOKEN"
```

Both routes accept any workspace token regardless of scope; the scope check
still applies to whatever you do next.

Accounts that share the workspace password open transparently on first use, so
they need none of this to get started. They still lock when their custody
window closes, and after that they need exactly this call — with the workspace
password as `accountPassword`.

The same is true after an explicit lock: `POST /v1/accounts/{slug}/lock` locks
any account, shared-password or not, and only unlock reopens it.

## Delete an account

```bash
curl -X DELETE "$API_URL/accounts/$ACCOUNT" -H "Authorization: Bearer $TOKEN"
```

Returns `204` and destroys the account's key material.
[Export first](/guides/export-key-material/) if anything needs to survive.

## Related

- [Accounts, wallets, and addresses](/concepts/accounts-wallets-addresses/) — the model
- [Accounts endpoints](/api/accounts/) — exact shapes

Source: https://tee.hypetrade.xyz/guides/manage-accounts-and-wallets/index.mdx
