Skip to main content
A transfer moves an amount from one available balance to another. It runs between your platform and an account you own, in either direction, and settles instantly against both balances. Transfers are how a platform pays out a share it collected and how it recovers one. For the end-to-end flow, see Split payments.
Transfers move money. A transfer debits the source balance immediately and cannot be cancelled or reversed. Recovering one means creating a second transfer in the opposite direction, which only succeeds while the funds are still there.

The two directions

There is no source 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:
A 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 the amount 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.
Summing 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.
Store the transfer_group against the order in your own database when you create the split. Listing does not filter on it, so your own record is what takes you from an order to its transfers.

Confirming

Each transfer emits transfer.created. The account is the event’s origin in both directions, so a platform subscribed with event_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 source available_balance is below amount in 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’s transfers capability is not active, your connect capability is not active on an outbound transfer, or the key lacks transfers:write.
  • NOT_FOUND (404), destination is not an account you own. An account belonging to another platform returns 404 rather than 403, 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 the kind filter on a list request is not payout or manual.
  • VALIDATION_ERROR (400), amount is not a positive decimal string, or a required field is missing.