Marketplace Splits
Split a payment payout across multiple balance accounts. Complete guide covering on-behalf creation, leg rules, holds, and settlement.
Marketplace splits let a partner divide a payment's payout across up to 3 balance accounts — for example, keeping a platform fee while sending the rest to a seller. Splits are configured per payment link with SplitConfiguration and executed at settlement time on top of the normal fee splits.
Concepts
Splits work as follows: the fee engine runs first (RoxPay, partner, scheme, and risk legs untouched), and the resulting Merchant payout remainder defines the net base. That net base is then replaced by your marketplace legs:
- Net base = gross amount minus every fee-service leg as-is, partner fees included.
- Absolute legs are paid in order against the remaining base.
- Any leftover becomes a
Merchant payoutremainder leg on the merchant account. - Percentage legs take
floor(pct / 100 * net)each; rounding dust goes to the last leg. - Overflow rule: a leg claiming more than the remainder sends the whole net base to the first leg and zeroes the rest.
- Zero-value legs are omitted (zero-amount splits are rejected).
On-Behalf Creation
Splits are partner-only and require an affiliated link: the payment link must be created on behalf of an affiliated individual or merchant in the partner's portfolio.
| API | On-behalf key | Format |
|---|---|---|
| v4 link create | AffiliatedUserCompanyId | USR-... (affiliated individual) or CMP-... (affiliated merchant) |
| v5 link create | AffiliatedUserId | USR-... (affiliated individual) or CMP-... (affiliated merchant) |
Rules:
- The key must start with
USR-orCMP-, otherwise link creation returns422. - Only partner or admin callers may use it — other callers get
403(AffiliatedUserNotInPortfolio). - The target must belong to the caller's partner portfolio, otherwise
403. - Split destinations are restricted to the partner's own balance account plus the balance accounts of its affiliated companies — anything else returns
422(MarketplaceSplitUnknownBalanceAccount).
Leg Rules
Each entry of SplitConfiguration accepts:
| Field | Type | Description |
|---|---|---|
BalanceAccountId | string | Destination balance account. Non-empty, max 64 chars. |
Amount | integer | Absolute share in minor units (cents). Positive integer. |
Percentage | number | Share of the net payout, (0, 100]. |
Hold | boolean | Lock the leg's funds until released via confirm. Default false. |
Reference | string | Label shown on the payment split. 1–64 chars, unique across legs. |
Constraints:
- Max 3 legs per link.
- EUR-only — other currencies return
422(MarketplaceSplitCurrencyNotSupported). - RoxDirect-only — omit
Providersor set it to["RoxDirect"], otherwise422(MarketplaceSplitProviderNotSupported). Amountlegs must add up to at most the gross link amount (the exact net base is only known at execution, once fees apply).Percentagelegs must sum to exactly 100.- No mixing
AmountandPercentagelegs in one configuration (422). - Every leg must set either
AmountorPercentage, never both, never neither. Holdmust be a boolean. Held legs must target the affiliated merchant balance account (422otherwise) and settle onto it locked against payout.Referencedefaults toMarketplace split N. Values must be unique (after defaulting) and must not reuse a reserved fee-leg name (case-insensitive):Merchant payout,RoxPay flat commission,RoxPay variable commission,RoxPay extra markup,Partner flat commission,Partner variable commission,Adyen processing fees,Remainder,Risk extra commission,Risk rolling reserve,Contractual rolling reserve.
Chargebacks and Refunds
- Chargebacks debit only the merchant leg:
platformChargebackLogictargets the first leg on the merchant (affiliated) balance account, falling back to the merchant account itself when no leg matches. - Refunds mirror every executed leg as-is.
Lifecycle: Settle to Pending to Confirm
- Create the link with
SplitConfiguration(plus the on-behalf key). - The customer pays; when the paylink settles, one lock row per held leg is queued with the executed amounts.
- List locked holds with
GET .../split-transfers/pending. - Release each hold with
POST .../split-transfers/confirm. Confirm only unlocks the amount on the merchant balance account — no funds move. It is single-use: confirming an already released hold returns409.
Create a Split Link — v4
curl -X POST https://app.roxpay.eu/api/v4/payments/link \
-H "Authorization: Bearer [REDACTED]" \
-H "Content-Type: application/json" \
-d '{
"Purpose": "Marketplace order #12345",
"Amount": 10000,
"Currency": "EUR",
"Providers": ["RoxDirect"],
"AffiliatedUserCompanyId": "CMP-abc123",
"SplitConfiguration": [
{ "BalanceAccountId": "BA00000000000000000000001", "Amount": 1000, "Reference": "Platform fee" },
{ "BalanceAccountId": "BA00000000000000000000002", "Amount": 9000, "Hold": true, "Reference": "Seller payout" }
],
"SuccessRedirectUrl": "https://yourshop.com/success",
"FailureRedirectUrl": "https://yourshop.com/failure",
"CancelRedirectUrl": "https://yourshop.com/cancel"
}'
Create a Split Link — v5
curl -X POST https://app.roxpay.eu/api/v5/payments/link \
-H "Authorization: Bearer [REDACTED]" \
-H "Content-Type: application/json" \
-d '{
"Purpose": "Marketplace order #12345",
"Amount": 10000,
"Currency": "EUR",
"Providers": ["RoxDirect"],
"AffiliatedUserId": "CMP-abc123",
"SplitConfiguration": [
{ "BalanceAccountId": "BA00000000000000000000001", "Percentage": 10, "Reference": "Platform fee" },
{ "BalanceAccountId": "BA00000000000000000000002", "Percentage": 90, "Hold": true, "Reference": "Seller payout" }
],
"SuccessRedirectUrl": "https://yourshop.com/success",
"FailureRedirectUrl": "https://yourshop.com/failure",
"CancelRedirectUrl": "https://yourshop.com/cancel"
}'
List Pending Holds — v4 and v5
curl -X GET "https://app.roxpay.eu/api/v4/split-transfers/pending?Limit=100" \
-H "Authorization: Bearer [REDACTED]"
curl -X GET "https://app.roxpay.eu/api/v5/split-transfers/pending?Limit=100" \
-H "Authorization: Bearer [REDACTED]"
Response (Holds lists locks owned by the calling partner company; Limit is 1–500, default 100):
{
"Holds": [
{
"HoldId": 12,
"RbcId": 345,
"LegIndex": 1,
"MerchantCompanyId": 348,
"PaymentReference": "TRX-20260916-ABCDEF12",
"BalanceAccountId": "BA00000000000000000000002",
"Reference": "Seller payout",
"AmountCents": 9000,
"Currency": "EUR",
"Status": "locked",
"CreatedOn": "2026-09-16T10:00:00",
"ProcessedOn": null
}
]
}
Reference carries the leg's SplitConfiguration label (empty when the leg used the default Marketplace split N name).
Confirm (Release) a Hold — v4 and v5
curl -X POST https://app.roxpay.eu/api/v4/split-transfers/confirm \
-H "Authorization: Bearer [REDACTED]" \
-H "Content-Type: application/json" \
-d '{ "HoldId": 12 }'
curl -X POST https://app.roxpay.eu/api/v5/split-transfers/confirm \
-H "Authorization: Bearer [REDACTED]" \
-H "Content-Type: application/json" \
-d '{ "HoldId": 12 }'
Response:
{
"Result": true,
"Hold": {
"HoldId": 12,
"Status": "released",
"Reference": "Seller payout",
"AmountCents": 9000,
"Currency": "EUR"
}
}
Errors
| Status | Case |
|---|---|
422 | SplitConfiguration is not a list of 1–3 objects, has unexpected fields, or a leg misses BalanceAccountId. |
422 | A leg sets both Amount and Percentage, or neither. |
422 | Amount is not a positive integer in minor units, or absolute legs exceed the gross link amount. |
422 | Percentage is not a number in (0, 100], or percentage legs do not sum to exactly 100. |
422 | Legs mix Amount and Percentage. |
422 | Hold is not a boolean, or a held leg does not target the affiliated merchant balance account. |
422 | Reference is empty, exceeds 64 chars, is duplicated, or reuses a reserved fee-leg name. |
422 | Split link is not EUR, or Providers is not exactly ["RoxDirect"]. |
422 | Split link misses the on-behalf key (AffiliatedUserCompanyId in v4, AffiliatedUserId in v5), or the key is not a USR-.../CMP-... uuid. |
422 | Confirm body misses HoldId or it fails schema validation. |
403 | Split link created by a non-partner caller, or the affiliated target is outside the caller's portfolio. |
404 | Confirm HoldId does not exist or is not owned by the calling partner company. |
409 | Confirm on an already released hold (single-use). |