Withdraw Fiat to Bank Account

Overview

You can build fiat withdrawals from your Go Account by submitting transaction details to BitGo. BitGo uses the data you pass to construct an unsigned transaction that wallet admins can approve. After video verification, a BitGo Bank & Trust operator signs the transaction with the user key. Then a different BitGo Bank & Trust operator downloads and signs the transaction in the BitGo Offline Vault Console (OVC). BitGo then uploads and broadcasts the transaction.

Fiat Withdrawal Methods

You can withdraw fiat from a Go Account to a bank account using either ACH or Wire transfers. The method BitGo uses depends on the destination bank account's supported payment rails.

FeatureACH WithdrawalWire Withdrawal
Use CaseLow-to-medium value, recurring withdrawals, and settlements that can wait 1–2 business days.Large or urgent withdrawals, cross-border payouts, and client settlements.
Settlement Time1–2 business days.Same day (if submitted before cutoff).
Bank SupportUS bank accounts only.US and international bank accounts.
CurrencyUSD to USD only.Varies by currency and region.
RequirementsBitGo-approved recipient bank.BitGo-approved destination bank. May require a reference/memo ID.

BitGo-Approved Bank Accounts

Fiat withdrawals to bank accounts require the use of BitGo-approved bank accounts. You can use the Add bank account endpoint to add a bank account.

When you add a bank account to an enterprise it goes through a screening process. BitGo approves most bank accounts within 48 hours and they can be used for fiat operations immediately after approval. You can see the status of the bank account by calling the Get bank account endpoint and checking the verificationState field.

Approval Requirements

To ensure your bank account is approved, verify the following requirements are met:

RequirementDescription
Valid Routing NumberFor US bank accounts, provide a valid 9-digit ABA routing number. BitGo validates routing numbers against the Federal Reserve's directory. Invalid routing numbers result in rejection.
Owner Name MatchThe ownerName must match the name of your end user. For example, if your enterprise is "John Doe", the bank account owner should be "John Doe".
Account TypeYou must specify the accountType field as checking or saving. This field is required for both ACH and wire transactions to process correctly.

Prerequisites

1. Add Bank Account

If you added a bank account to fund your Go Account and are using the same account for withdrawals you can skip this step.

Endpoint: Add bank account

export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export ENTERPRISE_ID="<CHILD_ENTERPRISE_ID>"

# Note: 021000021 is a valid sample routing number (JPMorgan Chase Bank)
curl -X POST \
  https://app.bitgo-test.com/api/v2/bankaccounts \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "type": "ach",
    "name": "JPMorgan Chase Bank",
    "accountNumber": "123456789012",
    "routingNumber": "021000021",
    "currency": "tfiatusd",
    "enterpriseId": "'"$ENTERPRISE_ID"'",
    "ownerName": "John Doe",
    "accountType": "checking",
    "description": "ACH deposits and withdrawals",
    "ownerAddressLine1": "123 Main Street",
    "ownerAddressLine2": "Suite 400",
    "ownerAddressCityLocality": "New York",
    "ownerAddressStateProvince": "NY",
    "ownerAddressPostalCode": "10001",
    "ownerAddressCountryCode": "US",
    "bankAddressLine1": "456 Financial Plaza",
    "bankAddressCityLocality": "New York",
    "bankAddressStateProvince": "NY",
    "bankAddressPostalCode": "10005",
    "bankAddressCountryCode": "US"
  }'
const BitGoJS = require('bitgo');

const bitgo = new BitGoJS.BitGo({
  accessToken: '<SERVICE_USER_ACCESS_TOKEN>',
  env: 'test',
});

// Note: 021000021 is a valid sample routing number (JPMorgan Chase Bank)
async function addBankAccount() {
  const bankAccount = await bitgo.post('/api/v2/bankaccounts').send({
    type: 'ach',
    name: 'JPMorgan Chase Bank',
    accountNumber: '123456789012',
    routingNumber: '021000021',
    currency: 'tfiatusd',
    enterpriseId: '<CHILD_ENTERPRISE_ID>',
    ownerName: 'John Doe',
    accountType: 'checking',
    description: 'ACH deposits and withdrawals',
    ownerAddressLine1: '123 Main Street',
    ownerAddressLine2: 'Suite 400',
    ownerAddressCityLocality: 'New York',
    ownerAddressStateProvince: 'NY',
    ownerAddressPostalCode: '10001',
    ownerAddressCountryCode: 'US',
    bankAddressLine1: '456 Financial Plaza',
    bankAddressCityLocality: 'New York',
    bankAddressStateProvince: 'NY',
    bankAddressPostalCode: '10005',
    bankAddressCountryCode: 'US',
  });

  console.log('Bank Account Added:', bankAccount);
  return bankAccount;
}

addBankAccount().catch(console.error);

Step Result

The response includes the bank account details and the verificationState field indicating the current approval status.

{
  "id": "60749ab75f8c4500060f5a8b244dd0cb",
  "idHash": "c2f4cf5555a66d77",
  "currency": "tfiatusd",
  "token": "tusd",
  "name": "JPMorgan Chase Bank",
  "shortCountryCode": "US",
  "accountNumber": "****9012",
  "enterpriseId": "1032e75c451052000436831deb797af1",
  "ownerName": "John Doe",
  "verificationState": "pending",
  "createdAt": "2025-01-12T15:30:00.000Z",
  "type": "ach",
  "routingNumber": "021000021",
  "accountType": "checking",
  "description": "ACH deposits and withdrawals",
  "ownerAddressLine1": "123 Main Street",
  "ownerAddressLine2": "Suite 400",
  "ownerAddressCityLocality": "New York",
  "ownerAddressStateProvince": "NY",
  "ownerAddressPostalCode": "10001",
  "ownerAddressCountryCode": "US",
  "bankAddressLine1": "456 Financial Plaza",
  "bankAddressCityLocality": "New York",
  "bankAddressStateProvince": "NY",
  "bankAddressPostalCode": "10005",
  "bankAddressCountryCode": "US",
  "virtualDepositOnly": false,
  "fee": {
    "amount": "1600",
    "individualFees": [
      {
        "type": "static",
        "amount": "1600"
      }
    ]
  }
}

Verification States

One of the following verification states is returned:

  • pending - Bank account is under review. Most accounts are approved within 48 hours.
  • approved - Bank account is approved and ready for fiat operations.
  • rejected - Bank account is rejected. Review the requirements and resubmit.
  • revise - Bank account requires corrections. Update the account and resubmit.
  • removed - Bank account is removed from the enterprise.

2. Get idHash

Endpoint: List Bank Accounts

export ENTERPRISE_ID="<ENTERPRISE_ID>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"

curl -X GET \
  https://app.bitgo-test.com/api/v2/bankaccounts?enterpriseId=$ENTERPRISE_ID \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Step Result

BitGo identifies your bank account with a unique idHash that you can pass as the address value in the next step. You also receive information about the available payment rails and associated bank fees.

{
    "bankAccounts": [
        {
            "type": "wire", // available payment rails vary by bank and region
            "accountNumber": "012345678",
            "address1": "test",
            "address2": "test",
            "enterpriseId": "1032e75c451052000436831deb797af1",
            "id": "60749ab75f8c4500060f5a8b244dd0cb",
            "name": "My Bank",
            "owner": {
                "name": "test",
                "address1": "test",
                "address2": "test"
            },
            "idHash": "c2f4cf5555a66d77",
            "routingNumber": "087654321",
            "shortCountryCode": "US",
            "verificationState": "approved",
            "fee": { // fees vary by payment rail and bank
              "amount": "string",
              "individualFees": [
                {
                  "type": "static",
                  "amount": "string"
                }
              ]
            },
            "feeInfo": {
              "coin": "fiatusd",
              "bank": "silvergate",
              "type": "static",
              "amount": "string"
            },
        }
    ]
}

3. Build Transaction

To withdraw assets from your Go Account to a bank account, you must build a transaction and send the assets.

Endpoint: Build a Transaction

export COIN="<FIAT_ASSET_ID>" # Fiat asset IDs in Go Accounts always start with "ofc" (such as ofctusdt)
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export AMOUNT="<AMOUNT_IN_BASE_UNITS>"
export ADDRESS="<ID_HASH>" # Use the `idHash`, returned in the prior step

curl -X POST \
  https://app.bitgo-test.com/api/v2/$COIN/wallet/$WALLET_ID/tx/build \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "recipients": [
      {
        "amount": "'"$AMOUNT"'",
        "address": "'"$ADDRESS"'"
      }
    ]
}'
let params = {
  recipients: [
    {
      amount: 1000,
      address: '<ID_HASH>',
    },
  ],
};
wallet.prebuildTransaction(params).then(function (transaction) {
  // print transaction details
  console.dir(transaction);
});

Step Result

{
  "payload": "{\"coin\":\"ofctusdt\",\"recipients\":[{\"address\":\"tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t\",\"amount\":\"1000\"}],\"fromAccount\":\"62c5b1de8a0c5200071c9a603bdbadc5\",\"nonce\":\"d8e5d930-f827-42b1-ad85-40544dad1be6\",\"timestamp\":\"2025-06-25T17:45:56.394Z\",\"feeString\":\"0\",\"shortCircuitBlockchainTransfer\":false,\"isIntraJXTransfer\":false}",
  "feeInfo": { "feeString": "0" },
  "coin": "ofc",
  "token": "ofctusdt"
}

4. Authenticate Transaction

Use your Go Account passphrase to authenticate the transaction. To ensure your passphrase isn't passed over the Internet, so you must use BitGo Express in external-signing mode or the JavaScript SDK. If you're using Express in external signing-mode, configure your Go Account passphrase as the WALLET_<walletId>_PASSPHRASE environment variable.

export BITGO_EXPRESS_HOST="<YOUR_LOCAL_HOST>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export WALLET_ID="<YOUR_WALLET_ID>"
export WALLET_PASSPHRASE="<YOUR_GO_ACCOUNT_PASSPHRASE>"

curl -X POST \
  http://$BITGO_EXPRESS_HOST/api/v2/ofc/signPayload \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
   "walletId": "'"$WALLET_ID"'",
   "walletPassphrase": "'"$WALLET_PASSPHRASE"'",
   "payload": "{\"coin\":\"ofcusdt\",\"recipients\":[{\"address\":\"tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t\",\"amount\":\"1000\"}],\"fromAccount\":\"67ab85b5190bf872e84da2b6e527d9f3\",\"nonce\":\"77d91a11-a7fa-4ad7-91e5-f054328717f9\",\"timestamp\":\"2025-06-26T15:02:06.924Z\",\"feeString\":\"0\",\"shortCircuitBlockchainTransfer\":false,\"isIntraJXTransfer\":false}"
}
// assuming that "bitgo" is an already initialized bitgo instance
  const wallet = await bitgo.coin('ofc').wallets().get({ id: goAccountId });

  if (wallet === undefined) {
    throw new Error(`Could not find OFC wallet ${goAccountId}`);
  }

  const walletPassphrase = "<YOUR_WALLET_PASSPHRASE>";
  const tradingAccount = wallet.toTradingAccount();
  const stringifiedPayload = JSON.stringify(<payload_returned_by_tx/build_api>);
  const signature = await tradingAccount.signPayload({
    payload: stringifiedPayload,
    walletPassphrase,
  });

Step Result

{
  "coin": "ofcusdt",
  "recipients": [
    {
      "address": "tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t",
      "amount": "1000"
    }
  ],
  "fromAccount": "62c5b1de8a0c5200071c9a603bdbadc5",
  "nonce": "77d91a11-a7fa-4ad7-91e5-f054328717f9",
  "timestamp": "2025-06-26T15:02:06.924Z",
  "feeString": "0",
  "shortCircuitBlockchainTransfer": false,
  "isIntraJXTransfer": false,
  "payload": "{\"coin\":\"ofcusdt\",\"recipients\":[{\"address\":\"tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t\",\"amount\":\"1\"}],\"fromAccount\":\"62c5b1de8a0c5200071c9a603bdbadc5\",\"nonce\":\"77d91a11-a7fa-4ad7-91e5-f054328717f9\",\"timestamp\":\"2025-06-26T15:02:06.924Z\",\"feeString\":\"0\",\"shortCircuitBlockchainTransfer\":false,\"isIntraJXTransfer\":false}",
  "signature": "20dda1e9558adb297f69020f94cbf4955fabec73478506347dbb5ac8e73b506fc908bbc48bde75b339b99f5de808ed34b2017055c9df198fd1f8b4e524899f9583"
}

5. Send Transaction

Endpoint: Send Half-Signed Transaction

export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export ADDRESS="<ID_HASH>"
export COIN="<FIAT_ASSET_ID>"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export ADDRESS="<ID_HASH>"
export AMOUNT="<AMOUNT_IN_BASE_UNITS>"

curl -X POST \
  https://app.bitgo-test.com/api/v2/$COIN/wallet/$WALLET_ID/tx/send \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "recipients": [
      {
        "address": "'"$ADDRESS"'",
        "amount": "'"$AMOUNT"'"
      }
    ],
    "halfSigned": {
      "payload": {
        "coin": "ofctusd",
        "recipients": [
          {
            "address": "'"$ADDRESS"'",
            "amount": "'"$AMOUNT"'"
          }
        ],
        "fromAccount": "<WALLET_ID>",
        "nonce": "f4af25d0-a037-4afc-8ce6-d7f158d826d1",
        "timestamp": "2021-09-10T22:45:20.777Z",
        "idfSignedTimestamp": "2021-09-10T22:45:20.113Z",
        "idfVersion": 1,
        "idfUserId": "<USER_ID>",
        "feeString": "1000"
      },
      "signature": "<signature>"
    }
  }'

Step Result

{
    "transfer": {
        "id": "<id>",
        "coin": "ofctusd",
        "wallet": "<wallet id>",
        "walletType": "trading",
        "enterprise": "603ca88c44f82002560378421dc7a191",
        "txid": "ddd6875812fa59f899a68059ff303bfa6d39fc350ee687c407383d0444b43d2b",
        "height": 999999999,
        "heightId": "999999999-613be001f3de560006842d52251d5d49",
        "date": "2021-09-10T22:45:21.342Z",
        "type": "send",
        "value": -10000,
        "valueString": "-10000",
        "baseValue": -10000,
        "baseValueString": "-10000",
        "feeString": "0",
        "payGoFee": 0,
        "payGoFeeString": "0",
        "usd": -100,
        "usdRate": 1,
        "state": "signed",
        "instant": false,
        "isReward": false,
        "isFee": false,
        "tags": [
            "603ca8c01b83cb000656311787dab4cd",
            "603ca88c44f82002560378421dc7a191"
        ],
        "history": [
            {
                "date": "2021-09-10T22:45:21.340Z",
                "action": "signed"
            },
            {
                "date": "2021-09-10T22:45:21.216Z",
                "user": "5e4ed695ae2e542100336fb16e586019",
                "action": "created"
            }
        ],
        "signedDate": "2021-09-10T22:45:21.340Z",
        "entries": [
            {
                "address": "address",
                "wallet": "603ca8c01b83cb000656311787dab4cd",
                "value": -10000,
                "valueString": "-10000"
            },
            {
                "address": "bf6cc5252438dc85",
                "value": 10000,
                "valueString": "10000"
            }
        ],
        "signedTime": "2021-09-10T22:45:21.340Z",
        "createdTime": "2021-09-10T22:45:21.216Z"
    },
    "txid": "ddd6875812fa59f899a68059ff303bfa6d39fc350ee687c407383d0444b43d2b",
    "tx": {
    "transaction": {
      "coin": "ofctusd",
      "recipients": [
        {
          "address": "<bank id hash>",
          "amount": "10000"
        }
      ],
      "fromAccount": "<wallet id>",
      "nonce": "f4af25d0-a037-4afc-8ce6-d7f158d826d1",
      "timestamp": "2021-09-10T22:45:20.777Z",
      "idfSignedTimestamp": "2021-09-10T22:45:20.113Z",
      "idfVersion": 1,
      "idfUserId": "<user id>",
      "feeString": "0"
    },
    "signature": "<signature>",
    "idfVersion": 1,
    "idfSignedTimestamp": "2021-09-10T22:45:20.113Z",
    "idfUserId": "<user id>"
  },
    "status": "signed"
}

6. Approve Transaction (Optional)

Note: If you configure an approval requirement for withdrawals, you can't approve your own transactions - another admin must approve them.

Endpoint: Update Pending Approval

export COIN="<ASSET_ID>"
export APPROVAL_ID="<APPROVAL_ID>"
export ACCESS_FIAT_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export OTP="<OTP>"

curl -X POST \
  http://api/v2/$COIN/pendingapprovals/$APPROVAL_ID
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -d '{
    "state": "approved",
    "otp": "'"$OTP"'"
}'
const baseCoin = this.bitgoSDK.coin(initialPendingApproval.coin);
const pendingApproval = await baseCoin.pendingApprovals().get({ id: initialPendingApproval.id });
const result = await pendingApproval.approve(params);

Step Result

Once approved, BitGo rebuilds the unsigned transaction, applying the most up-to-date fees.

{
  "id": "655686880765186f0b3e9e88e1bdd0f4",
  "coin": "tbtc4",
  "wallet": "6553e933288be490293ae748efafeaaf",
  "enterprise": "62c5ae8174ac860007aff138a2d74df7",
  "creator": "62ab90e06dfda30007974f0a52a12995",
  "createDate": "2023-11-16T21:15:52.703Z",
  "info": {
    "type": "transactionRequest",
    "transactionRequest": {
      "requestedAmount": "10000",
      "fee": 45242,
      "sourceWallet": "6553e933288be490293ae748efafeaaf",
      "policyUniqueId": "6553e933288be490293ae753",
      "recipients": [
        {
          "address": "2N3sBpM1RnWRMXnEVoUWnM7xtYzL756JE2Q",
          "amount": "10000",
          "_id": "655686880765186f0b3e9e8a"
        }
      ],
      "buildParams": {
        "recipients": [
          {
            "address": "2N3sBpM1RnWRMXnEVoUWnM7xtYzL756JE2Q",
            "amount": "10000"
          }
        ]
      },
      "coinSpecific": {
        "tbtc4": {
          "txHex": "01000000000101eac5e7d68acfc2349672fb99094e79c2006e5fd663475c8f1eb6f29095970b740100000000ffffffff02102700000000000017a914747e6b7f5db53794b0fc01d95878e0f9b6db96738746c1020000000000220020efb9deaabeab5d62cecc363f24cd6deafcdd14d93d8734f7f01ed3953e732a640500483045022100cd6c221e4cefbb51aa82087d86e7d089b2473eb21ae809dba89eb9f13c4caf19022000f3b7f1b7fec2dc49cbfce35baa69ca37ab9ee8e51c779cde5b98a653f3e142010000695221029bde55661e4f359cf9ca2d1846c235e752f760ed6d65e2018f821253e78b3c722103f2b4a813ab79e4fb46edf3a6a87fb154a85b45810158e949f41a3fce3c9bf574210399a6d766f6d3a3843f8479446ee766b5745dc15c24d1db5149214d27987bd29453ae00000000"
        }
      },
      "validTransaction": "01000000000101eac5e7d68acfc2349672fb99094e79c2006e5fd663475c8f1eb6f29095970b740100000000ffffffff02102700000000000017a914747e6b7f5db53794b0fc01d95878e0f9b6db96738746c1020000000000220020efb9deaabeab5d62cecc363f24cd6deafcdd14d93d8734f7f01ed3953e732a640400483045022100cd6c221e4cefbb51aa82087d86e7d089b2473eb21ae809dba89eb9f13c4caf19022000f3b7f1b7fec2dc49cbfce35baa69ca37ab9ee8e51c779cde5b98a653f3e14201473044022002b246c43722fec49858caf6b0605a0d01a6b6c13c964b84bf272604fd3951780220324ee44ba3ce8febb154c317e688a5f6ed410ad8e2bd77c5678bf1f7f3eb45f601695221029bde55661e4f359cf9ca2d1846c235e752f760ed6d65e2018f821253e78b3c722103f2b4a813ab79e4fb46edf3a6a87fb154a85b45810158e949f41a3fce3c9bf574210399a6d766f6d3a3843f8479446ee766b5745dc15c24d1db5149214d27987bd29453ae00000000",
      "validTransactionHash": "ff40ccd5c8ba75ffdce27d8584b6e8ee625cb3f71f7cbb6d072a445a97c2c8c3"
    }
  },
  "state": "approved",
  "scope": "wallet",
  "userIds": [
    "62ab90e06dfda30007974f0a52a12995",
    "627ff9325a5c1b0007c05a40d15e1522"
  ],
  "approvalsRequired": 1,
  "singleRunResults": [
    {
      "ruleId": "Custody Enterprise Transaction ID Verification",
      "triggered": false,
      "_id": "655686880765186f0b3e9e89"
    }
  ],
  "resolvers": [
    {
      "user": "627ff9325a5c1b0007c05a40d15e1522",
      "date": "2023-11-16T21:33:24.644Z",
      "resolutionType": "pending"
    }
  ]
}

See Also