# Vaults and wallets

A vault groups on-chain wallets under a single [Signing Policy](/api-reference#tag/signingpolicies). Each wallet is an MPC-generated address on one network family, `evm` or `solana`. The Crypto Vault holds every wallet key as MPC shares split across the three Key Store nodes of its MPC cluster; the whole key never exists anywhere. Note the two senses of the word: the Crypto Vault is the product, while a vault is the resource described on this page, the container that ties a set of wallets to one Signing Policy. See the full [Vaults reference](/api-reference#tag/vaults) and [Wallets reference](/api-reference#tag/wallets) for every operation.

## Create a vault

```
POST /v1/vaults
```

Requires all four authentication headers (see the [authentication guide](/develop/authentication/machine-users) for how to generate them).

| Field | Required | Description |
|---|---|---|
| `id` | yes | Caller-supplied, permanent vault ID. Choose an ID that is stable and meaningful in your system |
| `display_name` | yes | Human-readable name |
| `description` | no | Free-form description |

```bash
curl -s -X POST "$CV_BASE/v1/vaults" \
  -H "Authorization: ApiKey $API_KEY_UUID" \
  -H "X-Request-Timestamp: $TS_HEX" \
  -H "X-Request-Nonce: $NONCE_HEX" \
  -H "X-Request-Assertion: $ASSERTION" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "treasury-vault",
    "display_name": "Treasury vault",
    "description": "Operational treasury wallets"
  }'
```

This endpoint is asynchronous. If the response is `202`, [poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` until the status is no longer `"processing"`.

## List and get vaults

```
GET /v1/vaults
```

Query parameters (all optional):

| Parameter | Type | Description |
|---|---|---|
| `cursor` | string | Opaque cursor for keyset pagination |
| `limit` | integer | Page size |
| `vault_ids` | string | Comma-separated vault IDs to include |
| `is_frozen` | boolean | `true` returns only frozen vaults; `false` returns only active vaults |
| `display_name_like` | string | Case-insensitive substring match on display name |

The response is a standard page (`items`, `total`, `next_cursor`); see [Errors, pagination and idempotency](/develop/conventions/errors-pagination) for the paging convention.

```
GET /v1/vaults/{vault_id}
```

Returns the full vault object, including its `signing_policy`, its freeze state, and all of its wallets inline in the `wallets` array.

Both endpoints require only the `Authorization` header (no nonce, timestamp, or assertion):

```bash
curl -s "$CV_BASE/v1/vaults?is_frozen=false&limit=20" \
  -H "Authorization: ApiKey $API_KEY_UUID"
```

## Update a vault

```
PUT /v1/vaults/{vault_id}
```

Replaces the vault's mutable fields: `display_name` (required) and `description` (optional). Requires all four authentication headers.

```json
{
  "display_name": "Treasury vault (EU)",
  "description": "EU operational treasury wallets"
}
```

This endpoint is asynchronous. If the response is `202`, [poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` until the status is no longer `"processing"`.

## Generate a wallet

Before presenting network choices, call `GET /v1/system-info` and use its `supported_blockchain_networks` catalog. Do not hardcode networks or assets in your client. See [System settings](/develop/system-settings) for the response shape and enabled-state behavior.

```
POST /v1/vaults/{vault_id}/wallets
```

Requires all four authentication headers.

| Field | Required | Description |
|---|---|---|
| `display_name` | yes | Human-readable name |
| `network_family` | yes | `evm` or `solana`. Selects the signing curve and the coin type used to derive the wallet's address |
| `networks` | yes | The networks the wallet signs for, all within the chosen family (see the table below) |
| `description` | no | Free-form description |

There is no caller-supplied `id`: the wallet's identifier is its derived on-chain address, returned on creation. An `evm` wallet has one ECDSA secp256k1 address that is valid across all EVM networks; a `solana` wallet is an Ed25519 address.

Response excerpt:

```json
{
  "address": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
  "derivation_path": "m/44/60/0/0/0",
  "network_family": "evm",
  "networks": [
    { "id": "eip155:1", "name": "Ethereum" },
    { "id": "eip155:8453", "name": "Base" }
  ]
  /* plus display_name, description, vault, is_frozen, freeze_reason, and the audit fields */
}
```

This endpoint is asynchronous. If the response is `202`, [poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` until the status is no longer `"processing"`.

Submit only a family and networks whose catalog entries have `enabled: true`. In requests, a network is the name (case-insensitive) or the CAIP-2 ID, so `"ethereum"`, `"Ethereum"`, and `"eip155:1"` are equivalent. Responses always use the object form `{ "id": "eip155:1", "name": "Ethereum" }`.

The catalog keeps unavailable testnets visible with `is_testnet: true` and `enabled: false`. This lets your client explain that an administrator must enable testnets without allowing an invalid wallet request.

## List, get, and update wallets

```
GET /v1/vaults/{vault_id}/wallets
```

In addition to the standard `cursor` and `limit` pagination parameters, the list endpoint filters by:

| Parameter | Type | Description |
|---|---|---|
| `addresses` | string | Comma-separated wallet addresses to include |
| `networks` | string | Comma-separated networks to filter by |
| `network_families` | string | Comma-separated network families (`evm`, `solana`) to filter by |
| `is_frozen` | boolean | `true` returns only frozen wallets; `false` returns only active wallets |
| `display_name_like` | string | Case-insensitive substring match on display name |

```
GET /v1/vaults/{vault_id}/wallets/{address}
```

Returns the full wallet object. Both GET endpoints require only the `Authorization` header.

```
PUT /v1/vaults/{vault_id}/wallets/{address}
```

Updates a wallet's `display_name` (required), `networks` (required), and `description` (optional). This endpoint is asynchronous. If the response is `202`, [poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` until the status is no longer `"processing"`.

## Delete wallets

```
DELETE /v1/vaults/{vault_id}/wallets
```

Deletes multiple wallets from the vault in a single call. Requires all four authentication headers. The body lists the addresses to delete:

```json
{ "addresses": ["0x8ba1f109551bD432803012645Ac136ddd64DBA72"] }
```

This endpoint is asynchronous. If the response is `202`, [poll the request](/api-reference#tag/requests) via `GET /v1/requests/{request_id}` until the status is no longer `"processing"`.

Every vault and wallet operation is recorded in the audit log. [Export the audit log](/develop/audit-export) with the `vault_ids` or `wallet_addresses` filters to review a vault's or a wallet's full history.

## Freeze

Freezing works at both scopes: `POST /v1/vaults/freeze` halts every wallet in the listed vaults, and `POST /v1/vaults/{vault_id}/wallets/freeze` halts individual wallets. A frozen wallet, or any wallet in a frozen vault, receives `403` from both wallet sign endpoints until it is unfrozen. See [Freeze and unfreeze](/develop/freeze).

## Sign with a wallet

Once a wallet exists, sign transactions with `POST /v1/vaults/{vault_id}/wallets/{address}/sign`; see [Sign a transaction](/develop/signing/sign-transaction). Every signature is evaluated against the vault's Signing Policy first. To check which vaults a caller may manage, call `GET /v1/whoami`: the `manage_keys_and_wallets_for_vaults` field lists the vaults for which the caller may import, generate, and manage keys and wallets.
