Create Settlements

Overview

Settlement between your partner platform and BitGo must occur during a pre-determined settlement window, typically once every 24 hours. At that time you create settlements for all trading that occurs on your platform. Settlements transfer assets off chain between your BitGo account to client BitGo accounts.

Settlement API Versions

There are two settlement endpoints. Which one to use depends on whether your integration has disputes enabled:

VersionEndpointUse when
v1POST /api/network/v1/enterprises/{enterpriseId}/partners/settlementsDisputes are not enabled for your integration. BitGo processes settlement synchronously.
v2POST /api/network/v2/enterprises/{enterpriseId}/partners/settlementsYour integration has disputes enabled. Requires cutoffAt. BitGo processes settlement asynchronously after the dispute window ends.

If you call the v1 endpoint and your integration has disputes enabled, you receive a 403 response.

📘

Note

You can use the v2 endpoint for synchronized settlements by setting a dispute window of 0.

Signature and Verification

Settlement requests from you to BitGo must be cryptographically signed and verified to provide integrity, authenticity, and nonrepudiation. Use the BitGo JavaScript SDK or Express to sign the payload with the trading account private key.

Prerequisites

Use the following steps to generate a payload and send a signed settlement request.

1. Get Signing Payload

Add the externalId, settlementAmounts, and nonce attributes to the request. If you are submitting a v2 settlement (disputes enabled), also include cutoffAt.

Endpoint: Get Signing Payload for Partner Settlement

Note: If your HTTP client does not support sending a request body on a GET request, use the POST variant: POST .../partners/settlements-signing.

curl -X GET http://$BITGO_EXPRESS_HOST/api/network/v1/enterprises/{enterpriseId}/partners/settlements-signing \
-H "Authorization: Bearer v123xyz..." \
-H "Content-Type: application/json" \
-d '{
  "externalId": "string",
  "notes": "string",
  "settlementAmounts": {
    "additionalProp": {
      "additionalProp": "string"
    }
  },
  "nonce": "string"
}'
curl -X GET http://$BITGO_EXPRESS_HOST/api/network/v1/enterprises/{enterpriseId}/partners/settlements-signing \
-H "Authorization: Bearer v123xyz..." \
-H "Content-Type: application/json" \
-d '{
  "externalId": "string",
  "notes": "string",
  "settlementAmounts": {
    "additionalProp": {
      "additionalProp": "string"
    }
  },
  "nonce": "string",
  "cutoffAt": "2026-03-18T12:00:00.000Z"
}'
import superagent from 'superagent';
import { BitGoAPI } from '@bitgo/sdk-api';
import { coins } from '@bitgo/sdk-core';
require('dotenv').config({ path: '../../.env' });

const bitgo = new BitGoAPI({
  accessToken: process.env.TESTNET_ACCESS_TOKEN,
  env: 'test',
});

const coin = 'ofc';
bitgo.register(coin, coins.Ofc.createInstance);

function getWalletPwFromEnv(walletId: string): string {
  const name = `WALLET_${walletId}_PASSPHRASE`;
  const walletPw = process.env[name];
  if (walletPw === undefined) {
    throw new Error(`Could not find wallet passphrase ${name} in environment`);
  }
  return walletPw;
}

async function main() {
  const enterpriseId = '<YOUR_ENTERPRISE_ID>';
  const walletId = 'xyz123';
  const wallet = await bitgo.coin(coin).wallets().get({ id: walletId });
  const walletPassphrase = getWalletPwFromEnv(wallet.id());
  const tradingAccount = wallet.toTradingAccount();

  const settlementBody = {
    externalId: 'idForSettlementAtPartner',
    notes: 'this is a settlement note',
    settlementAmounts: {
      '09684b38-49ed-4cf6-a28e-c3a459494c69': {
        BTC: '100',
        ETH: '-200',
      },
      '136c1c69-8654-484f-8c5f-591b334a084f': {
        XRP: '300',
        BTC: '-400',
      },
    },
    nonce: '1',
  };

  // Step 1: Get signing payload
  const signingResponse = await superagent
    .get(`${process.env.BITGO_EXPRESS_HOST}/api/network/v1/enterprises/${enterpriseId}/partners/settlements-signing`)
    .set('Authorization', `Bearer ${process.env.TESTNET_ACCESS_TOKEN}`)
    .set('Content-Type', 'application/json')
    .send(settlementBody);

  const { payload } = signingResponse.body;

  // Sign the payload
  const signature = tradingAccount.signPayload({
    payload,
    walletPassphrase,
  });

  // Step 2: Send signed settlement payload
  const settlementResponse = await superagent
    .post(`${process.env.BITGO_EXPRESS_HOST}/api/network/v1/enterprises/${enterpriseId}/partners/settlements`)
    .set('Content-Type', 'application/json')
    .send({
      ...settlementBody,
      payload,
      signature,
    });

  console.log('Settlement:', settlementResponse.body);
}
import superagent from 'superagent';
import { BitGoAPI } from '@bitgo/sdk-api';
import { coins } from '@bitgo/sdk-core';
require('dotenv').config({ path: '../../.env' });

const bitgo = new BitGoAPI({
  accessToken: process.env.TESTNET_ACCESS_TOKEN,
  env: 'test',
});

const coin = 'ofc';
bitgo.register(coin, coins.Ofc.createInstance);

function getWalletPwFromEnv(walletId: string): string {
  const name = `WALLET_${walletId}_PASSPHRASE`;
  const walletPw = process.env[name];
  if (walletPw === undefined) {
    throw new Error(`Could not find wallet passphrase ${name} in environment`);
  }
  return walletPw;
}

async function main() {
  const enterpriseId = '<YOUR_ENTERPRISE_ID>';
  const walletId = 'xyz123';
  const wallet = await bitgo.coin(coin).wallets().get({ id: walletId });
  const walletPassphrase = getWalletPwFromEnv(wallet.id());
  const tradingAccount = wallet.toTradingAccount();

  const settlementBody = {
    externalId: 'idForSettlementAtPartner',
    notes: 'this is a settlement note',
    settlementAmounts: {
      '09684b38-49ed-4cf6-a28e-c3a459494c69': {
        BTC: '100',
        ETH: '-200',
      },
      '136c1c69-8654-484f-8c5f-591b334a084f': {
        XRP: '300',
        BTC: '-400',
      },
    },
    nonce: '1',
    cutoffAt: '2026-03-18T12:00:00.000Z',
  };

  // Step 1: Get signing payload
  const signingResponse = await superagent
    .get(`${process.env.BITGO_EXPRESS_HOST}/api/network/v1/enterprises/${enterpriseId}/partners/settlements-signing`)
    .set('Authorization', `Bearer ${process.env.TESTNET_ACCESS_TOKEN}`)
    .set('Content-Type', 'application/json')
    .send(settlementBody);

  const { payload } = signingResponse.body;

  // Sign the payload
  const signature = tradingAccount.signPayload({
    payload,
    walletPassphrase,
  });

  // Step 2: Send signed settlement payload
  const settlementResponse = await superagent
    .post(`${process.env.BITGO_EXPRESS_HOST}/api/network/v2/enterprises/${enterpriseId}/partners/settlements`)
    .set('Content-Type', 'application/json')
    .send({
      ...settlementBody,
      payload,
      signature,
    });

  console.log('Settlement:', settlementResponse.body);
}

Step Result

{
  "payload": "{ \"externalId\": \"...\", \"settlementAmounts\": { ... }, \"nonce\": \"...\" }"
}

2. Send Signed Settlement Payload

Endpoints: Perform Partner Settlement (v1) · Initiate Partner Settlement (v2)

cutoffAt is the ISO 8601 timestamp of the most recent trade in this settlement. BitGo uses it to inform clients of the trading activity in this settlement. Include it in both the signing request (Step 1) and the settlement request body so the signature covers it.

curl -X POST http://$BITGO_EXPRESS_HOST/api/network/v1/enterprises/{enterpriseId}/partners/settlements \
-H "Content-Type: application/json" \
-d '{
  "externalId": "idForSettlementAtPartner",
  "notes": "this is a settlement note",
  "settlementAmounts": {
    "09684b38-49ed-4cf6-a28e-c3a459494c69": {
      "BTC": "100",
      "ETH": "-200"
    },
    "136c1c69-8654-484f-8c5f-591b334a084f": {
      "XRP": "300",
      "BTC": "-400"
    }
  },
  "nonce": "1",
  "payload": "<payload string from step 1>",
  "signature": "<signature>"
}'
curl -X POST http://$BITGO_EXPRESS_HOST/api/network/v2/enterprises/{enterpriseId}/partners/settlements \
-H "Content-Type: application/json" \
-d '{
  "externalId": "idForSettlementAtPartner",
  "notes": "this is a settlement note",
  "settlementAmounts": {
    "09684b38-49ed-4cf6-a28e-c3a459494c69": {
      "BTC": "100",
      "ETH": "-200"
    },
    "136c1c69-8654-484f-8c5f-591b334a084f": {
      "XRP": "300",
      "BTC": "-400"
    }
  },
  "nonce": "1",
  "cutoffAt": "2026-03-18T12:00:00.000Z",
  "payload": "<payload string from step 1>",
  "signature": "<signature>"
}'

Step Result

The v2 response has an initial status of pending, and BitGo processes it asynchronously:

  1. When the dispute window ends, BitGo makes a call to your finalize settlement endpoint. This call includes the final settlement amounts which may differ from the initially posted settlement amounts. There may be prior settlement disputes closing or clients submitting disputes on this current settlement.

  2. The settlement enters the top up window, where you can reconcile disputes on your platform.

  3. Once the top up window ends, BitGo attempts to process the settlement. If funds in your Go Account are not sufficient to satisfy your liabilities for settlement, the settlement will move to the failed state and the settlement will be retried on a continual basis until the settlement goes through.

{
  "settlement": {
    "id": "string",
    "partnerId": "string",
    "externalId": "string",
    "status": "completed",
    "settlementType": "onchain",
    "reconciled": true,
    "initiatedBy": "string",
    "notes": "string",
    "createdAt": "2025-11-11T19:12:37.874Z",
    "updatedAt": "2025-11-11T19:12:37.874Z",
    "finalizedAt": "2025-11-11T19:12:37.874Z",
    "rtId": "string",
    "lossSLAAlertSent": true,
    "gainSLAAlertSent": true,
    "cutoffAt": null,
    "disputed": false
  }
}
{
  "settlement": {
    "id": "string",
    "partnerId": "string",
    "externalId": "string",
    "status": "pending",
    "settlementType": "onchain",
    "reconciled": false,
    "initiatedBy": "string",
    "notes": "string",
    "createdAt": "2026-03-18T12:00:00.000Z",
    "updatedAt": "2026-03-18T12:00:00.000Z",
    "finalizedAt": null,
    "rtId": "string",
    "lossSLAAlertSent": false,
    "gainSLAAlertSent": false,
    "cutoffAt": "2026-03-18T12:00:00.000Z",
    "disputed": false
  }
}

Next

Deallocate Assets

See Also