RoxPay API
Guides

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 payout remainder 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.

APIOn-behalf keyFormat
v4 link createAffiliatedUserCompanyIdUSR-... (affiliated individual) or CMP-... (affiliated merchant)
v5 link createAffiliatedUserIdUSR-... (affiliated individual) or CMP-... (affiliated merchant)

Rules:

  • The key must start with USR- or CMP-, otherwise link creation returns 422.
  • 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:

FieldTypeDescription
BalanceAccountIdstringDestination balance account. Non-empty, max 64 chars.
AmountintegerAbsolute share in minor units (cents). Positive integer.
PercentagenumberShare of the net payout, (0, 100].
HoldbooleanLock the leg's funds until released via confirm. Default false.
ReferencestringLabel 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 Providers or set it to ["RoxDirect"], otherwise 422 (MarketplaceSplitProviderNotSupported).
  • Amount legs must add up to at most the gross link amount (the exact net base is only known at execution, once fees apply).
  • Percentage legs must sum to exactly 100.
  • No mixing Amount and Percentage legs in one configuration (422).
  • Every leg must set either Amount or Percentage, never both, never neither.
  • Hold must be a boolean. Held legs must target the affiliated merchant balance account (422 otherwise) and settle onto it locked against payout.
  • Reference defaults to Marketplace 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: platformChargebackLogic targets 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

  1. Create the link with SplitConfiguration (plus the on-behalf key).
  2. The customer pays; when the paylink settles, one lock row per held leg is queued with the executed amounts.
  3. List locked holds with GET .../split-transfers/pending.
  4. 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 returns 409.
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"
  }'
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

StatusCase
422SplitConfiguration is not a list of 1–3 objects, has unexpected fields, or a leg misses BalanceAccountId.
422A leg sets both Amount and Percentage, or neither.
422Amount is not a positive integer in minor units, or absolute legs exceed the gross link amount.
422Percentage is not a number in (0, 100], or percentage legs do not sum to exactly 100.
422Legs mix Amount and Percentage.
422Hold is not a boolean, or a held leg does not target the affiliated merchant balance account.
422Reference is empty, exceeds 64 chars, is duplicated, or reuses a reserved fee-leg name.
422Split link is not EUR, or Providers is not exactly ["RoxDirect"].
422Split link misses the on-behalf key (AffiliatedUserCompanyId in v4, AffiliatedUserId in v5), or the key is not a USR-.../CMP-... uuid.
422Confirm body misses HoldId or it fails schema validation.
403Split link created by a non-partner caller, or the affiliated target is outside the caller's portfolio.
404Confirm HoldId does not exist or is not owned by the calling partner company.
409Confirm on an already released hold (single-use).

On this page