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.
-
You must confirm or reject each fiat withdrawal before BitGo processes the transaction. You can automate this by subscribing to the organization
transferwebhook and calling the Confirm fiat withdrawal endpoint whenever you receive a fiat withdrawal event.If you configure a liveness check policy for automatic approvals, withdrawals that pass liveness verification confirm automatically.
-
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>"
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 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 Withdrawal
export WALLET_ID="<YOUR_WALLET_ID>"
export TRANSFER_ID="<YOUR_TRANSFER_ID>"
export SERVICE_USER_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export ACTION="<APPROVE_OR_REJECT>"
curl -X PUT \
https://app.bitgo-test.com/api/v2/wallet/$WALLET_ID/fiatwithdrawals/$TRANSFER_ID/confirm \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $SERVICE_USER_TOKEN" \
-d '{
"action":"'"$ACTION"'"
}'
// 7. Approve Transaction (Optional)
export COIN="<ASSET_ID>"
export APPROVAL_ID="<APPROVAL_ID>"
export SERVICE_USER_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',
});
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 REST API for this step (see cURL tab)
// 3. Build Transaction
let params = {
recipients: [
{
amount: 1000,
address: '<ID_HASH>',
},
],
};
wallet.prebuildTransaction(params).then(function (transaction) {
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 REST API for this step (see cURL tab)
// 6. Confirm Withdrawal
// Use the REST API for this step (see cURL tab)
// 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 Withdrawal Response (Approve)
{
"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"
}
// 6. Confirm Withdrawal Response (Reject)
{
"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": "rejected"
}
// 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
}