Skip to main content

Accounts API

An account is a custody vault. See Accounts & vaults for the concepts.

MethodPathScope
POST/v1/accountsaccounts:write
GET/v1/accountsaccounts:read
GET/v1/accounts/{id}accounts:read
PATCH/v1/accounts/{id}accounts:write
POST/v1/accounts/{id}/assets/{assetCanonical}accounts:write
POST/v1/accounts/{id}/addressesaddresses:write
GET/v1/accounts/{id}/addressesaddresses:read
GET/v1/accounts/{id}/balancesbalances:read
GET/v1/accounts/{id}/balances/{assetCanonical}balances:read
DELETE/v1/accounts/{id}accounts:write

Create an account

POST /v1/accounts
{
"label": "merchant-42-hot",
"purpose": "hot",
"vaultType": "web3",
"firstAssetCanonical": "USDT_ETHEREUM",
"externalRef": "mch_42"
}
FieldNotes
labelRequired, unique per workspace.
purposeOptional: hot / warm / cold / escrow / gas / generic (default).
vaultTypeOptional but recommended: web3 (EVM) or asset (non-EVM). Inferred from firstAssetCanonical if omitted.
firstAssetCanonicalOptional — activates this asset immediately.
externalRefOptional, unique per workspace — your own reference (merchant/user id).
{
"id": "8f1c...uuid",
"workspaceId": "...",
"label": "merchant-42-hot",
"purpose": "hot",
"tags": { "vaultType": "web3" },
"disabled": false,
"createdAt": "2026-05-27T10:00:00.000Z",
"activations": [
{ "id": "...", "accountId": "8f1c...", "assetCanonical": "USDT_ETHEREUM", "status": "active" }
],
"addresses": []
}

:::info Partial success If the vault is created upstream but the immediate asset activation fails, the response is still 201 with a firstAssetError: { assetCanonical, message } field. Retry activation via POST /v1/accounts/{id}/assets/{assetCanonical} — don't recreate the account, the vault already exists. :::

Look up an account

GET /v1/accounts accepts exact-match filters — resolve a known vault without paging the workspace:

QueryMatches
?label=merchant-42-hotthe account with that exact label
?externalRef=mch_42the account with that exact external reference

Both are unique per workspace, so a match returns at most one item.

Activate an asset

Before an account can hold or receive an asset, activate it:

POST /v1/accounts/{id}/assets/USDT_ETHEREUM
→ {
"accountId": "8f1c...uuid",
"assetCanonical": "USDT_ETHEREUM",
"status": "active",
"createdAt": "2026-05-27T10:00:30.000Z"
}

Idempotent — calling again for an already-active asset is a no-op.

Generate a deposit address

POST /v1/accounts/{id}/addresses
{ "assetCanonical": "USDT_ETHEREUM" }
→ {
"id": "...",
"accountId": "8f1c...",
"assetCanonical": "USDT_ETHEREUM",
"address": "0x9a8b...c3d4",
"memo": null,
"status": "active",
"createdAt": "2026-05-27T10:01:00.000Z"
}

memo is populated for memo/tag-based chains (XRP, some Cosmos assets); funds sent without it may be unattributable.

Read balances

GET /v1/accounts/{id}/balances/USDT_ETHEREUM
→ {
"accountId": "8f1c...",
"assetCanonical": "USDT_ETHEREUM",
"available": "1500.00",
"total": "1500.00",
"fetchedAt": "2026-05-27T10:05:00.000Z"
}

Balances are read live from custody at request time (fetchedAt is that moment). available is spendable now; total includes amounts reserved by in-flight transfers. GET …/balances (no asset) returns an array across all activated assets.

Delete

DELETE /v1/accounts/{id} removes the ZenVault account, its addresses, and activations. The underlying custody vault is not deleted and any funds there remain — sweep funds out first. Use delete to prune test/mistake vaults.