The two directions
There is nosource field. The debited side is always whoever is authenticated, so the direction follows from how you authenticate.
To an account. Authenticate as your platform and name the account in destination.
Back to your platform. Send X-Account-Id to act as the account, and set destination to self.
A transfer between two accounts is rejected with 400. Moving value from one to the other is two transfers through your platform.
What a transfer requires
The account needs
transfers active in both directions, because the capability governs its participation rather than one side of it. Your connect capability is only checked on the outbound direction, so a restricted platform can still recover funds it is liable for. See Become a platform.
Rules
One currency, no conversion.currency has to be a currency both balances already hold. A transfer never converts, and never creates a balance in a currency the destination did not hold.
Available balance only. Transfers draw on available_balance, not pending_balance. A charge has to settle before its funds can be transferred. See Balances.
Never below zero. A transfer above the source’s available_balance is rejected with INSUFFICIENT_BALANCE. No debt is recorded, so once an account withdraws its balance, that money cannot be recovered. A lost dispute is the one exception to this floor: it can drive available_balance negative directly, and while any currency is negative, every transfer for that party is blocked, not only in that currency. See Disputes.
Amounts are decimal strings in currency, greater than zero, e.g. "7000.00".
Status
status is paid once the movement has been recorded against both balances, and pending while it has not. It is derived from the underlying movement rather than stored, so it cannot disagree with the balances.
What kind of movement
kind says what a transfer is, since direction alone cannot say it.
kind is not a request field. POST /v1/transfers always creates manual; payout is written only by settlement. The platform’s own cut of a sale is never a transfer: it is its own object, read at Platform fees. A direct charge settles no transfer at all for the platform’s cut, and a destination charge settles at most one payout transfer.
Filter the list to one kind:
kind outside payout or manual is rejected with 400. This includes platform_fee: that value is retired, and the platform’s cut now lives at Platform fees instead.
What a payout transfer carries
A destination charge states its split one of two ways, and theamount on the payout transfer it settles depends on which:
platform_fee(fee-first). The account is meant to keep the gross minus your cut, but the transfer itself carries the sale’s full gross. Your cut is struck separately, as its own platform fee record, and is not subtracted from the transfer amount.transfer_data.amount(share-first). The transfer carries exactly what the account was contracted to receive, its net. No platform fee record exists for this charge.
payout transfers for an account tells you what it was credited on, not what it earned from customers: on a fee-first split, that sum is too high by exactly your platform fee for those charges. To get the account’s true net across a mix of split styles, subtract its platform fees for the same charges from the sum of its payout transfers.
Grouping
transfer_group tags a transfer as part of a set. Use the id of the charge that funded the shares, and reuse it on every transfer for that charge, including a later recovery. Every transfer returns the group you set.
GET /v1/transfers · scope transfers:read · full field reference →
Returns transfers your platform was a party to, newest first. Pass connected_account_id to narrow to one account, in either direction.
Confirming
Each transfer emits transfer.created. The account is the event’s origin in both directions, so a platform subscribed withevent_source connect and the account subscribed with account both receive it, whichever side created the transfer.
Do not treat a network or
5xx error as proof the transfer was not created. Verify with Get Transfer before retrying, or retry with the same Idempotency-Key.Errors
Transfers return the standard error envelope. Common cases:INSUFFICIENT_BALANCE(400), the sourceavailable_balanceis belowamountin that currency. Check the balance and the settlement date, then retry. See Balances.ORGANIZATION_IN_DEBT(400), the transfer’s source has a negative balance in some currency, even one other than the transfer’s own. Every transfer out is blocked until that currency’s balance clears. See Disputes.FORBIDDEN(403), the account’stransferscapability is notactive, yourconnectcapability is notactiveon an outbound transfer, or the key lackstransfers:write.NOT_FOUND(404),destinationis not an account you own. An account belonging to another platform returns404rather than403, so the response never confirms the id exists.BAD_REQUEST(400), the two sides are not a platform and one of its own accounts, or thekindfilter on a list request is notpayoutormanual.VALIDATION_ERROR(400),amountis not a positive decimal string, or a required field is missing.

