Before implementing cash balance transfers, ensure you have:
- Authentication scopes -
cash_balance_transfers:adminto create and cancel transfers.cash_balance_transfers:readto retrieve and list them.positions:read: to check the cash balance.
- Webhook handler ready - Set up a webhook endpoint to listen for transfer lifecycle events and subscribe to
CASH_BALANCE_TRANSFER.*events.
Retrieve the cash balance of the source account group and check that available_for_withdrawal covers the amount you intend to transfer.
Example request for the source cash balance
GET /account_groups/{account_group_id}/payments/cash_balances
{
"meta": {
"count": 1,
"total_count": 1,
"offset": 0,
"limit": 100
},
"data": [
{
"account_group_id": "2596db3b-0d03-4651-9eda-970910479dfb",
"currency": "EUR",
"balance": "250",
"locked_for_trading": "50",
"pending_settlement": "0",
"available_for_withdrawal": "200",
"available_for_trading": "200"
}
]
}In this example, EUR 200.00 can be transferred. The remaining EUR 50.00 is reserved for a pending order as shown by the locked_for_trading value.
Create the transfer by specifying the source and target account groups, the amount, and the currency. The request requires an idempotency-key header so that a retried request does not create a second transfer.
Example request to create the transfer
POST /payments/cash_balance_transfers
{
"source_account_group_id": "2596db3b-0d03-4651-9eda-970910479dfb",
"target_account_group_id": "e56c14b3-8370-4c04-bc67-7092f7ec113c",
"amount": "200.00",
"currency": "EUR"
}The request fields are as follows.
| Field | Required | Description |
|---|---|---|
source_account_group_id | Required | The account group the cash is transferred from. Must differ from the target_account_group_id. |
target_account_group_id | Required | The account group the cash is transferred to. Must belong to the same user and client as the source, and be of the same account group type. Only PERSONAL, CHILD, or BUSINESS account group types are supported. |
amount | Required | The amount to transfer, expressed as a whole number or with exactly two decimal places. Must not exceed the source account group’s available_for_withdrawal amount. |
currency | Required | Three-letter ISO 4217 currency code. This field supports EUR (euro) and GBP (pound sterling). |
The response returns the transfer object, which adds the following fields.
| Field | Required | Description |
|---|---|---|
id | N/A | Unique identifier of the transfer. Use it as your reference for the transfer. |
status | N/A | Reflects the transfer’s current status. This field supports ISSUED, CONFIRMED, and CANCELLED. |
cancellation_reason | N/A | The reason the transfer was cancelled. Present only when the status is CANCELLED. Otherwise, the value is null. |
created_at, updated_at | N/A | When the transfer was created and last updated. |
All validation happens before any cash moves, so a rejected request never creates a transfer. A request is rejected with a 400 error code if any of the following occurs:
- The source and target account group are the same.
- The account groups do not belong to the same holder.
- The source and target account group types do not match.
- The account group types are different than either
PERSONAL,CHILD, orBUSINESS. - The source account group does not hold enough settled cash.
Webhooks notify you of events in business processes, not of the success or failure of initialising them. Check the HTTP status of the response to your request and handle failure cases accordingly.
Rather than polling, listen for the following events:
CASH_BALANCE_TRANSFER.ISSUEDCASH_BALANCE_TRANSFER.CONFIRMEDCASH_BALANCE_TRANSFER.CANCELLED
Each event carries the full transfer details in the object field.
Example event for a confirmed transfer
{
"id": "a59c81ee-7770-4f6b-9ebe-8c268b78beb4",
"created_at": "2026-06-25T14:15:23Z",
"type": "CASH_BALANCE_TRANSFER.CONFIRMED",
"object": {
"id": "b3f6a0c2-1d7e-4a3b-9f21-0c2d4e6f8a10",
"created_at": "2026-06-25T14:15:22Z",
"updated_at": "2026-06-25T14:15:23Z",
"source_account_group_id": "2596db3b-0d03-4651-9eda-970910479dfb",
"target_account_group_id": "e56c14b3-8370-4c04-bc67-7092f7ec113c",
"amount": "200.00",
"currency": "EUR",
"status": "CONFIRMED",
"cancellation_reason": null
},
"webhook_id": "9df39835-be87-4243-9018-f2500b39cee6"
}You will also receive a CASH_BALANCE.UPDATED event for each of the two account groups once the transfer is confirmed.
You can retrieve details of a specific cash balance transfer, and list all transfers for an end user or a business, at any time.
To retrieve a specific transfer:
GET /payments/cash_balance_transfers/{cash_balance_transfer_id}.
To retrieve a list of transfers for an end user:
GET /users/{user_id}/payments/cash_balance_transfers.
To retrieve a list of transfers for a business:
GET /businesses/{business_id}/payments/cash_balance_transfers.
List results can be sorted by created_at or id.
A transfer can only be cancelled while it has the status ISSUED.
Example request to cancel a cash balance transfer
DELETE /payments/cash_balance_transfers/{cash_balance_transfer_id}
A successful cancellation returns a 204 No Content status code. No cash is moved, the transfer’s status becomes CANCELLED, and cancellation_reason explains why. Attempting to cancel a transfer that is already CONFIRMED or CANCELLED is rejected.