Crypto-as-a-Service

Overview

You can build on-chain crypto 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.

Prerequisites

Cookbook

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

Withdraw Crypto On ChainOpen Cookbook

1. Build Transaction

To withdraw assets from your Go Account to an on-chain address, you must build a transaction and send the assets.

Endpoint: Build a Transaction

export COIN="<CRYPTO_ASSET_ID>" # Crypto asset IDs in Go Accounts always start with "ofc" (such as ofctbtc)
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export AMOUNT="<AMOUNT_IN_BASE_UNITS>"
export ADDRESS="<DESTINATION_ADDRESS>"

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"'"
      }
    ]
}'

Step Result

{
  "payload": "{\"coin\":\"ofctbtc\",\"recipients\":[{\"address\":\"tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t\",\"amount\":\"1\"}],\"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": "ofctbtc"
}

2. Authenticate Transaction

Use your Go Account passphrase to authenticate the transaction. To ensure your passphrase isn't passed over the Internet, you must use BitGo Express in external-signing mode, the JavaScript SDK, or Java. 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\":\"ofctbtc\",\"recipients\":[{\"address\":\"tb1q5mwcr2749nnm5c5d6rzx5h92l2u9ex5jzygzd39vfv9qerj5fmjqwlgy4t\",\"amount\":\"190000\"}],\"fromAccount\":\"67ab85b5190bf872e84da2b6e527d9f3\",\"nonce\":\"77d91a11-a7fa-4ad7-91e5-f054328717f9\",\"timestamp\":\"2025-06-26T15:02:06.924Z\",\"feeString\":\"0\",\"shortCircuitBlockchainTransfer\":false,\"isIntraJXTransfer\":false}"
}

Step Result

{
  "coin": "ofctbtc",
  "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,
  "payload": "{\"coin\":\"ofctbtc\",\"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"
}

3. Send Transaction

Endpoint: Send Half-Signed Transaction

export COIN="<CRYPTO_ASSET_ID>"
export WALLET_ID="<YOUR_WALLET_ID>"
export ACCESS_TOKEN="<SERVICE_USER_ACCESS_TOKEN>"
export ADDRESS="<DESTINATION_ADDRESS>"
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": "ofctbtc",
        "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": "ofctbtc",
        "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": "ofctbtc",
      "recipients": [
        {
          "address": "<DESTINATION_ADDRESS>",
          "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"
}

4. 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="<CRYPTO_ASSET_ID>"
export APPROVAL_ID="<APPROVAL_ID>"
export ACCESS_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"'"
}'

Step Result

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

{
  "id": "655686880765186f0b3e9e88e1bdd0f4",
  "coin": "tbtc",
  "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": {
        "tbtc": {
          "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