Skip to content

Create accounts and derive wallets

Create and delete accounts, derive and import wallets, read addresses, set tags.

Updated View as Markdown

Auth: workspace token on every route. Mutations need write; reads need read.

POST /v1/accounts

Create an account. Scope: write. Returns 201.

Request

{
  "displayName": "Treasury",
  "kind": "HD",
  "defaultNetwork": "ethereum",
  "hasOwnPassword": false
}
Field Type Required Notes
displayName string yes Human label. Must normalize to 4–64 characters and contain at least one Latin letter — the account slug is derived from it
kind "HD" | "PK" no Defaults to HD
secret string no Mnemonic for HD, private key for PK. Omit on HD to generate; required on PK
defaultNetwork string no Network slug. Must be one the workspace defines — a well-formed slug for a network it does not have is rejected
hasOwnPassword boolean no Give the account a lock separate from the session
accountPassword string no Required when hasOwnPassword is set, and rejected without it. 12–256 characters, held to the same policy as a workspace password

Response

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

The slug is generated by the service. Read it from the response — it is the identifier every later call uses.

DELETE /v1/accounts/{slug}

Delete an account and its key material. Scope: write. Returns 204.

POST /v1/accounts/{slug}/wallets

Derive wallets. Scope: write. Returns 201.

{ "count": 5 }

count is a positive integer, maximum 500 per call.

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

Reporting both sides makes the call easy to reconcile after an ambiguous network failure.

POST /v1/accounts/{slug}/wallets/import

Import a private key as one wallet. Scope: write. Returns 201.

{ "privateKey": "0x…" }
{
  "wallet": {
    "id": 2,
    "tags": [],
    "addresses": [
      { "publicKey": "0xabc…", "vm": "evm", "network": "ethereum", "chainId": 1 }
    ]
  }
}

GET /v1/accounts/{slug}/wallets

List wallets with their addresses. Scope: read. Returns 200.

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

GET /v1/accounts/{slug}/wallets/{id}/addresses

Addresses for one wallet. Scope: read. Returns 200.

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

{id} must be a canonical non-negative integer — 0, 1, 2. Aliases of a number are rejected with 400 and invalid_parameter before the account is looked up, so 01, 1.0, 1e0, and 0x1 all fail. An unknown but well-formed id returns 404 with account_not_found.

PUT /v1/accounts/{slug}/wallets/{id}/tags

Replace a wallet’s tags. Scope: write. Returns 200.

{ "tags": ["hot", "treasury"] }

Up to 32 tags, each 1–64 characters after normalization. The list is replaced, not merged — send {"tags":[]} to clear. The request rejects unknown fields.

Tags are trimmed, Unicode-normalized, and de-duplicated before they are stored, so the list you get back is not always the list you sent. Compare against the response, not against your request.

{
  "wallet": {
    "id": 1,
    "tags": ["hot", "treasury"],
    "addresses": [ ]
  }
}

Errors

Status Code
400 invalid_body, invalid_slug, invalid_parameter, invalid_private_key, invalid_mnemonic
401 session_expired
403 scope_denied
404 account_not_found
409 quota_wallets_exceeded
422 unsupported_for_kind, weak_password
423 account_locked
Navigation

Type to search…

↑↓ navigate↵ selectEsc close