Move Funds Between Your Accounts
Use this to fund a sub-account from the main account, sweep a sub-account's balance back to the main account, or move funds from one sub-account to another. It's an internal transfer: no PIX and no on-chain transaction, so it completes in seconds.
Before you start
- You need the account ID of the destination. For a sub-account, it's the
subAccountIdreturned when you created it (or listed byGET /v2/account/sub-accounts). For the main account, it's the top-levelidofGET /v2/account/account-infocalled withoutsubAccountId. - Both accounts must be yours: the main account and its sub-accounts. An account ID from outside your main account is refused.
- Internal transfers don't count against your monthly limits.
- The token is the same on both sides: BRLA, USDC, USDT or EURC.
1. Quote
The account that sends the funds is the one in the request. To send from a sub-account, add subAccountId; to send from the main account, leave it out.
GET /v2/account/quote/fixed-rate?inputCurrency=BRLA&inputPaymentMethod=INTERNAL&inputAmount=10&outputCurrency=BRLA&outputPaymentMethod=INTERNAL&blockchainSendMethod=PERMIT&inputThirdParty=false&outputThirdParty=false
blockchainSendMethod=PERMIT is required even though nothing goes on-chain. Without it the quote returns 400 InvalidFieldError: inputCurrency-inputPaymentMethod:blockchainSendMethod is invalid.
The response (sandbox, trimmed):
{
"quoteToken": "eyJ...",
"inputCurrency": "BRLA",
"inputPaymentMethod": "INTERNAL",
"inputAmount": "10",
"outputCurrency": "BRLA",
"outputPaymentMethod": "INTERNAL",
"outputAmount": "10",
"appliedFees": [
{ "type": "Markup Fee", "amount": "0", "currency": "BRLA" },
{ "type": "In Fee", "amount": "0", "currency": "BRLA" },
{ "type": "Conversion Fee", "amount": "0", "currency": "BRLA" },
{ "type": "Out Fee", "amount": "0", "currency": "BRLA" },
{ "type": "Gas Fee", "amount": "0", "currency": "BRLA" }
],
"feeSettlement": "IMMEDIATE"
}
Check appliedFees in your own quote: fees depend on your contract.
2. Ticket
Send the quote token and the destination account ID in ticketBlockchainOutput.beneficiaryWalletId. Despite its name, for an internal transfer that field takes an account ID, not a wallet address. If the quote had subAccountId, the ticket needs the same one, as a query parameter.
POST /v2/account/tickets/
{
"quoteToken": "eyJ...",
"ticketBlockchainOutput": {
"beneficiaryWalletId": "DESTINATION_ACCOUNT_ID"
}
}
{
"id": "819ea5fe-63ee-4657-b16d-b037980857a3",
"expiration": "2026-10-25T18:48:49.485459337Z"
}
3. Confirm
Read the ticket (with the sender's subAccountId, if any) until status is PAID, or wait for the TICKET-COMPLETE webhook:
GET /v2/account/tickets/819ea5fe-63ee-4657-b16d-b037980857a3
In sandbox both directions reached PAID within a few seconds.
Example: sweep a sub-account back to the main account
GET /v2/account/quote/fixed-rate?inputCurrency=BRLA&inputPaymentMethod=INTERNAL&inputAmount=10&outputCurrency=BRLA&outputPaymentMethod=INTERNAL&blockchainSendMethod=PERMIT&inputThirdParty=false&outputThirdParty=false&subAccountId=SUB_ACCOUNT_ID
POST /v2/account/tickets/?subAccountId=SUB_ACCOUNT_ID
{
"quoteToken": "eyJ...",
"ticketBlockchainOutput": {
"beneficiaryWalletId": "MAIN_ACCOUNT_ID"
}
}
{
"id": "6e722e7d-4014-4ed3-84cd-bde0e5454286",
"expiration": "2026-10-25T18:49:07.562907141Z"
}
GET /v2/account/tickets/6e722e7d-4014-4ed3-84cd-bde0e5454286?subAccountId=SUB_ACCOUNT_ID then returned status PAID.
Which IDs go where
| From → To | subAccountId (quote and ticket) | beneficiaryWalletId |
|---|---|---|
| Main → sub-account | not sent | the sub-account ID |
| Sub-account → main | the sub-account ID | the main account ID |
| Sub-account A → sub-account B | A's ID | B's ID |
Errors you may see
| Error | Cause |
|---|---|
400 InvalidFieldError: inputCurrency-inputPaymentMethod:blockchainSendMethod is invalid | blockchainSendMethod missing from the quote. |
400 InvalidFieldError: ticketBlockchainOutput.beneficiaryWalletId is invalid | The ID isn't your main account or one of its sub-accounts. |
400 InvalidFieldError: quoteToken is invalid | The quote and the ticket don't carry the same subAccountId. |
More on owners and destinations in Accounts and Sub-accounts.