Custody Starter Architecture

Overview

Whitelist policies restrict which addresses a wallet can send funds to. In the custody starter architecture, you create bidirectional whitelists between wallets so funds can only flow between designated wallets.

This guide covers creating whitelists for:

  • The custody wallet to allow withdrawals only to the standby wallet.
  • The standby wallet to allow withdrawals to the custody wallet and the deposit/withdraw wallet.

Custody-Standby Whitelist

Create bidirectional whitelists between the custody wallet and standby wallet so funds can only flow between these two wallets.

Prerequisites

Cookbooks

Need just the steps? Expand a cookbook below to get started:

Create Whitelists: Custody ↔ StandbyOpen Cookbook Create Whitelists: Standby ↔ Deposit/WithdrawOpen Cookbook

Step 1: Create Whitelist on Custody Wallet

Add the standby wallet address to the custody wallet whitelist. This ensures funds from the custody wallet can only go to the standby wallet.

Endpoint: Add Wallet-Policy Rule

export COIN="<ASSET_ID>"
export CUSTODY_WALLET_ID="<CUSTODY_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export STANDBY_WALLET_ADDRESS="<STANDBY_WALLET_ADDRESS>"

curl -X POST \
  "https://app.bitgo-test.com/api/v2/$COIN/wallet/$CUSTODY_WALLET_ID/policy/rule" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
  "coin": "'"$COIN"'",
  "id": "Custody to Standby Whitelist",
  "type": "advancedWhitelist",
  "condition": {
    "add": {
      "type": "address",
      "item": "'"$STANDBY_WALLET_ADDRESS"'",
      "metaData": {
        "label": "Standby Wallet"
      }
    }
  },
  "action": {
    "type": "deny"
  }
}'

Step Result

The whitelist policy is now active on the custody wallet. BitGo automatically denies any withdrawal attempt to an address not on the whitelist.

{
  "id": "66e9f4bb549f6c3957f8977fe3ed6ab4",
  "coin": "btc",
  "label": "Custody Wallet - Cold Storage",
  "admin": {
    "policy": {
      "rules": [
        {
          "id": "Custody to Standby Whitelist",
          "lockDate": "2025-09-18T22:05:21.219Z",
          "coin": "btc",
          "type": "advancedWhitelist",
          "action": { "type": "deny", "userIds": [] },
          "condition": {
            "entries": [
              {
                "item": "bc1p...",
                "type": "address",
                "metaData": {
                  "label": "Standby Wallet"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Note

For self-custody wallets, new whitelist policies lock 48 hours after creation and can only be unlocked by BitGo support. Custody wallets have different lock periods managed by BitGo.

Step 2: Create Whitelist on Standby Wallet (to Custody)

Add the custody wallet address to the standby wallet whitelist. This allows funds to flow back from the standby wallet to the custody wallet.

Endpoint: Add Wallet-Policy Rule

export COIN="<ASSET_ID>"
export STANDBY_WALLET_ID="<STANDBY_WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export CUSTODY_WALLET_ADDRESS="<CUSTODY_WALLET_ADDRESS>"

curl -X POST \
  "https://app.bitgo-test.com/api/v2/$COIN/wallet/$STANDBY_WALLET_ID/policy/rule" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
  "coin": "'"$COIN"'",
  "id": "Standby to Custody Whitelist",
  "type": "advancedWhitelist",
  "condition": {
    "add": {
      "type": "address",
      "item": "'"$CUSTODY_WALLET_ADDRESS"'",
      "metaData": {
        "label": "Custody Wallet"
      }
    }
  },
  "action": {
    "type": "deny"
  }
}'

Step Result

{
  "id": "6849948ac0623f81f74f63dbd8351d4f",
  "coin": "btc",
  "label": "Standby Wallet - Hot",
  "admin": {
    "policy": {
      "rules": [
        {
          "id": "Standby to Custody Whitelist",
          "lockDate": "2025-09-18T22:05:21.219Z",
          "coin": "btc",
          "type": "advancedWhitelist",
          "action": { "type": "deny", "userIds": [] },
          "condition": {
            "entries": [
              {
                "item": "bc1q...",
                "type": "address",
                "metaData": {
                  "label": "Custody Wallet"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Note

You add the deposit/withdraw wallet address to the standby wallet whitelist in the Standby-Deposit tab.

Verify Whitelists

Use the Get Wallet endpoint to verify whitelist policies.

Endpoint: Get Wallet by ID

export COIN="<ASSET_ID>"
export WALLET_ID="<WALLET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"

curl -X GET \
  "https://app.bitgo-test.com/api/v2/$COIN/wallet/$WALLET_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Step Result

Check the admin.policy.rules array in the response to confirm the whitelist entries.

{
  "id": "6849948ac0623f81f74f63dbd8351d4f",
  "coin": "btc",
  "label": "Standby Wallet - Hot",
  "admin": {
    "policy": {
      "rules": [
        {
          "id": "Standby to Custody Whitelist",
          "type": "advancedWhitelist",
          "condition": {
            "entries": [
              {
                "item": "bc1q...",
                "type": "address",
                "metaData": { "label": "Custody Wallet" }
              },
              {
                "item": "bc1p...",
                "type": "address",
                "metaData": { "label": "Deposit-Withdraw Wallet" }
              }
            ]
          }
        }
      ]
    }
  }
}

Whitelist Behavior

When you configure a whitelist policy with action.type: "deny":

  • Transactions to addresses on the whitelist proceed normally (subject to other policies).
  • Transactions to addresses not on the whitelist are automatically denied.

You can also configure whitelists with action.type: "getApproval" to require admin approval for non-whitelisted addresses instead of denying them outright.

Verify Complete Architecture

At this point, your custody starter architecture is fully configured. Verify the whitelist configuration:

Wallet Can Send To
Custody Wallet Standby Wallet only
Standby Wallet Custody Wallet, Deposit/Withdraw Wallet
Deposit/Withdraw Wallet Any address (no whitelist)

Fund Flow Summary

With the complete architecture in place, funds flow as follows:

┌─────────────────────────────────────────────────────────────┐
│                    FUND FLOW DIAGRAM                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  INBOUND (Deposits):                                        │
│  External ──────────────────────► Deposit/Withdraw Wallet   │
│                                                             │
│  INTERNAL (Accumulation):                                   │
│  Deposit/Withdraw ─────────────► Standby Wallet             │
│  Standby Wallet ───────────────► Custody Wallet             │
│                                                             │
│  INTERNAL (Replenishment):                                  │
│  Custody Wallet ───────────────► Standby Wallet             │
│  Standby Wallet ───────────────► Deposit/Withdraw Wallet    │
│                                                             │
│  OUTBOUND (Withdrawals):                                    │
│  Deposit/Withdraw Wallet ──────► External (Customers)       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Next

Create Receive Addresses

See Also