Skip to content

How to manage accounts and wallets

Create accounts, derive or import wallets, tag them, and unlock the ones with their own password.

Updated View as Markdown

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

Create an account

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:

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

Derive wallets

A new HD account has none. Derive in bulk:

curl -X POST "$API_URL/accounts/$ACCOUNT/wallets" \
  -H "Authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{"count":5}'
{ "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:

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:

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

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

Each wallet carries its addresses inline:

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

For one wallet’s addresses only:

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:

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:

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:

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

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

Returns 204 and destroys the account’s key material. Export first if anything needs to survive.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close