Custody API

Vaults & Wallets

A vault is an organizational container; a wallet is an on-chain address inside a vault. Wallets are either generated with keys held in your EnQlave signer, or registered from outside with a cryptographic proof that you control the address.

Vaults

POST/v1/vault/accounts

{"name": "Treasury", "metadata": {"team": "finance"}}   // name required, 1–255 chars
// 201 →
{"id": "…", "tenantId": "…", "name": "Treasury", "status": "ACTIVE",
 "metadata": {"team": "finance"}, "createdAt": "…", "updatedAt": "…"}
EndpointPurpose
GET/v1/vault/accounts?cursor=…List — {"vaults":[…],"page":{"nextCursor","hasMore"}}
GET/v1/vault/accounts/{vaultAccountId}Fetch one
PUT/v1/vault/accounts/{vaultAccountId}Rename / update metadata
POST/v1/vault/accounts/{vaultAccountId}/archiveArchive (status → ARCHIVED)

Generate a wallet

POST/v1/vault/accounts/{vaultAccountId}/wallets/generate

Creates a new address whose key is generated and held inside your EnQlave signer — never by Qustody.

{"assetId": "QC", "label": "ops-hot"}   // assetId required
// 201 →
{"id": "…", "vaultId": "…", "tenantId": "…", "assetId": "QC",
 "address": "0x…", "publicKey": "<hex, 1952 bytes>", "label": "ops-hot",
 "status": "ACTIVE", "createdAt": "…"}

Register an external wallet (ownership proof)

POST/v1/vault/accounts/{vaultAccountId}/wallets

Brings an existing address under management. You must prove you control the key by signing a deterministic challenge:

// challenge = Keccak256("qustody:register:" + lowercase(address))
// note: the lowercased address INCLUDES its 0x prefix

{
  "assetId":   "QC",                       // required
  "address":   "0xAb5801a7D398351b8bE11C439e05C5b3259aeC9B",  // required
  "publicKey": "<hex>",                    // required — 1952-byte public key
  "signature": "<hex>",                    // required — 3293-byte signature over the challenge
  "label":     "cold-1"
}

Verification is strict: the address must equal the address derived from publicKey (last 20 bytes of its Keccak-256 hash), and the signature must verify over the 32-byte challenge. Failures return 400 INVALID_OWNERSHIP_PROOF (code 1105). Hex values here may include or omit a 0x prefix.

EnQlave does this for you The EnQlave desktop app computes the challenge, signs it with the selected Qey, and calls this endpoint — registering by hand is only needed for custom signer integrations.

Wallet types

TypeBehavior
ENQLAVE_MANAGEDKey lives in EnQlave; can sign and send
REGISTEREDExternal key registered with ownership proof; can sign and send
WATCH_ONLYMonitored address; can never be a transaction source

Read endpoints

EndpointPurpose
GET/v1/vault/accounts/{vaultAccountId}/walletsList wallets in a vault (paginated)
GET/v1/wallets/{walletId}Fetch one wallet
POST/v1/wallets/{walletId}/archiveArchive a wallet
GET/v1/wallets/{walletId}/balanceLive on-chain balance
// GET /v1/wallets/{walletId}/balance →
{"walletId": "…", "assetId": "QC", "balance": "42000000000000000000",
 "blockNumber": 3392847, "queriedAt": "2026-08-06T12:00:00Z"}