Crypto-as-a-Service: Withdraw Fiat to Bank Account
Withdraw fiat from your Go Account to an approved bank account through ACH or wire transfer. See the Guide.
-
Add a BitGo-approved bank account to your enterprise for fiat withdrawals. Bank accounts go through a screening process and BitGo typically approves them within 48 hours. The account must meet requirements including a valid routing number, matching owner name, and specified account type.
If you previously added a bank account for funding and plan to use the same account for withdrawals, you can skip this step.
-
Retrieve the list of bank accounts for your enterprise to get the unique
idHashidentifier. ThisidHashis used as theaddressvalue when building the withdrawal transaction in the next step. -
Build the fiat withdrawal transaction by specifying the amount and destination bank account (using the
idHashas the address). BitGo constructs an unsigned transaction with the provided details. -
Authenticate the transaction using your Go Account passphrase. For security, never transmit the passphrase over the Internet. Use BitGo Express in external-signing mode or the JavaScript SDK to sign the payload locally.
-
Submit the half-signed transaction to BitGo for processing. The transaction includes the signed payload from the previous step and BitGo Bank & Trust operators review it before final approval.
-
Wire withdrawals require confirmation before BitGo processes them. Use the Confirmation API to programmatically approve the withdrawal. This security measure ensures outgoing fiat transfers are intentional and that BitGo authorizes them.
Note: If you configure a liveness check policy, withdrawals that pass liveness verification automatically confirm without needing this API call.
-
If you configured an approval requirement for withdrawals, another wallet admin must approve the transaction. Note that you cannot approve your own transactions when you enable approval policies.
// 1. Add Bank Account
export SERVICE_USER_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 $SERVICE_USER_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"
}'
// 2. Get idHash
export ENTERPRISE_ID="<ENTERPRISE_ID>"
export SERVICE_USER_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 $SERVICE_USER_TOKEN"
// 3. Build 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 SERVICE_USER_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 $SERVICE_USER_TOKEN" \
-d '{
"recipients": [
{
"amount": "'"$AMOUNT"'",
"address": "'"$ADDRESS"'"
}
]
}'
// 4. Authenticate Transaction
export BITGO_EXPRESS_HOST="<YOUR_LOCAL_HOST>"
export SERVICE_USER_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 $SERVICE_USER_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}"
}
// 5. Send Transaction
export WALLET_ID="<YOUR_WALLET_ID>"
export SERVICE_USER_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export ADDRESS="<ID_HASH>"
export COIN="<FIAT_ASSET_ID>"
export WALLET_ID="<YOUR_WALLET_ID>"
export SERVICE_USER_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 $SERVICE_USER_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>"
}
}'
// 6. Confirm Wire Withdrawal
export WALLET_ID="<YOUR_WALLET_ID>"
export TRANSFER_ID="<YOUR_TRANSFER_ID>"
export SERVICE_USER_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
curl --request PUT \
--url https://app.bitgo-test.com/api/v2/wallet/$WALLET_ID/wirewithdrawals/$TRANSFER_ID/confirm \
--header "accept: application/json" \
--header "content-type: application/json" \
--header "authorization: Bearer $ACCESS_TOKEN" \
--data '{"action":"approve"}'
// 7. Approve Transaction (Optional)
export COIN="<ASSET_ID>"
export APPROVAL_ID="<APPROVAL_ID>"
export ACCESS_FIAT_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export OTP="<OTP>"
curl -X POST \
https://app.bitgo-test.com/api/v2/pendingapprovals/$APPROVAL_ID \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $SERVICE_USER_TOKEN" \
-d '{
"state": "approved",
"otp": "'"$OTP"'"
}'
// 1. Add Bank Account
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);
// 2. Get idHash
// Use the List Bank Accounts endpoint to get the idHash
// 3. Build Transaction
let params = {
recipients: [
{
amount: 1000,
address: '<ID_HASH>',
},
],
};
wallet.prebuildTransaction(params).then(function (transaction) {
// print transaction details
console.dir(transaction);
});
// 4. Authenticate Transaction
// 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,
});
// 5. Send Transaction
// Use the Send Half-Signed Transaction endpoint
// 6. Confirm Wire Withdrawal
// Use the Update wire withdrawal endpoint to confirm the withdrawal
// 7. Approve Transaction (Optional)
const baseCoin = this.bitgoSDK.coin(initialPendingApproval.coin);
const pendingApproval = await baseCoin.pendingApprovals().get({ id: initialPendingApproval.id });
const result = await pendingApproval.approve(params);
// 1. Add Bank Account Response
{
"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"
}
// 2. Get idHash Response
{
"bankAccounts": [
{
"type": "wire",
"accountNumber": "012345678",
"enterpriseId": "1032e75c451052000436831deb797af1",
"id": "60749ab75f8c4500060f5a8b244dd0cb",
"name": "My Bank",
"idHash": "c2f4cf5555a66d77",
"routingNumber": "087654321",
"shortCountryCode": "US",
"verificationState": "approved"
}
]
}
// 3. Build Transaction Response
{
"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 Response
{
"coin": "ofcusdt",
"recipients": [
{
"address": "tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t",
"amount": "1000"
}
],
"fromAccount": "62c5b1de8a0c5200071c9a603bdbadc5",
"nonce": "77d91a11-a7fa-4ad7-91e5-f054328717f9",
"timestamp": "2025-06-26T15:02:06.924Z",
"feeString": "0",
"signature": "20dda1e9558adb297f69020f94cbf4955fabec73478506347dbb5ac8e73b506fc908bbc48bde75b339b99f5de808ed34b2017055c9df198fd1f8b4e524899f9583"
}
// 5. Send Transaction Response
{
"transfer": {
"id": "<id>",
"coin": "ofctusd",
"wallet": "<wallet id>",
"walletType": "trading",
"enterprise": "603ca88c44f82002560378421dc7a191",
"txid": "ddd6875812fa59f899a68059ff303bfa6d39fc350ee687c407383d0444b43d2b",
"date": "2021-09-10T22:45:21.342Z",
"type": "send",
"value": -10000,
"valueString": "-10000",
"state": "signed"
},
"txid": "ddd6875812fa59f899a68059ff303bfa6d39fc350ee687c407383d0444b43d2b",
"status": "signed"
}
// 6. Confirm Wire Withdrawal Response
{
"id": "6980dfc5decaa85472a63ce93f0a07a7",
"coin": "tfiatusd",
"wallet": "6967b45a4ffdedb5747d5934e544b94c",
"walletType": "trading",
"enterprise": "6967afd69792fa80c629f22bb52caed6",
"txid": "a10d7719179b5b6705b10a6bcd2cb492c10cae15564d567d4dd3e8e699718c64",
"date": "2026-02-02T17:33:05.285Z",
"type": "send",
"subType": "ofc_withdrawal",
"value": -2100,
"valueString": "-2100",
"state": "pendingApproval"
}
// 7. Approve Transaction (Optional) Response
{
"id": "655686880765186f0b3e9e88e1bdd0f4",
"coin": "tbtc4",
"wallet": "6553e933288be490293ae748efafeaaf",
"enterprise": "62c5ae8174ac860007aff138a2d74df7",
"creator": "62ab90e06dfda30007974f0a52a12995",
"createDate": "2023-11-16T21:15:52.703Z",
"state": "approved",
"scope": "wallet",
"approvalsRequired": 1
}