Marketplace & Split Payments
Onboard submerchants, split captures across sellers, and pay out to each seller.
Kvell's marketplace flow lets a single checkout split across multiple recipients β a platform selling on behalf of several sellers keeps its own cut and routes the rest straight to each seller's settlement balance, without ever touching the money itself. Splitting, submerchant onboarding, and payouts are the three moving pieces; this guide walks through all three with a full worked example.
How splits work
- A payment session created with a
splitsarray is a split payment. Each entry names asubmerchantIdand itsamountMinorshare; whatever is left after summing the splits stays with the platform (the parent merchant). - Every split recipient is a submerchant β a seller onboarded under your platform merchant account with its own identity, KYC status, and settlement balance. A submerchant only receives money once its KYC status is active; an unapproved submerchant listed in
splitsfails the session-create call. splits[].amountMinormust sum to no more than the sessionamount. Kvell never infers a split β leave a seller out and they get nothing from that payment.- Everything you already use for a normal payment β 3DS, installments, capture, refund β runs unchanged underneath. Splitting only changes how the resulting proceeds are distributed internally. See Accepting Card Payments if you haven't built that flow yet.
Onboard a submerchant
Submerchant creation, identity capture, and KYC review happen in the merchant dashboard console β there's no signed public-API call for it. Once a submerchant clears KYC and is active, it participates in your integration two ways:
- as a split recipient β pass its
submerchantIdinsidesplitson a payment session - as a payout recipient β pass its
submerchantIdtoPOST /v1/payouts
Each submerchant also gets its own API key, scoped to itself only. To confirm what kind of caller a key belongs to:
curl https://api.pay.kvell.group/v1/auth/whoami \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:00:00Z" \
-H "X-Signature: <signature>"{
"subjectScope": "merchant",
"merchantId": "mrc_8a1b2c3d4e5f",
"apiKeyId": "key_708192a3b4c5"
}subjectScope is "merchant" for your platform key and "submerchant" for a seller's own key. A submerchant's KYC decision reads the same way yours does β GET /v1/kyc/status, covered in Going Live.
Worked example: a 1,200 TRY order split between two sellers
A marketplace platform sells a 1,200.00 TRY (120000 kuruΕ) order made up of items from two sellers. Seller A's share is 700.00 TRY, seller B's is 400.00 TRY, and the platform keeps the remaining 100.00 TRY as its take rate.
| Party | submerchantId | Share (kuruΕ) | Share (TRY) |
|---|---|---|---|
| Seller A | sub_7f3a9c21 | 70000 | 700.00 |
| Seller B | sub_2b88e04d | 40000 | 400.00 |
| Platform (remainder) | β | 10000 | 100.00 |
| Total | 120000 | 1,200.00 |
1. Create the split payment session
curl -X POST https://api.pay.kvell.group/v1/payments/sessions \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:00:00Z" \
-H "X-Signature: <signature>" \
-H "Content-Type: application/json" \
-d '{
"transactionId": "ORDER-2026-4521",
"amount": 120000,
"currency": "TRY",
"description": "Order #4521 (2 sellers)",
"returnUrl": "https://marketplace.example.com/return",
"splits": [
{ "submerchantId": "sub_7f3a9c21", "amountMinor": 70000 },
{ "submerchantId": "sub_2b88e04d", "amountMinor": 40000 }
]
}'{
"sessionId": "4d4e8a9c-1f2a-7b8c-9d0e-1f3f1c9a2e6b",
"checkoutUrl": "https://pay.kvell.group/s/4d4e8a9c-1f2a-7b8c-9d0e-1f3f1c9a2e6b",
"expiresAt": "2026-04-13T10:30:00Z",
"amount": 120000,
"currency": "TRY",
"status": "active"
}Redirect the buyer to checkoutUrl. Everything downstream β 3DS, installments, capture β behaves exactly like a normal hosted checkout.
2. Receive the capture webhook
When the buyer completes payment, Kvell fires the same payment.captured event you'd get for any card charge:
X-Kvell-Event: payment.captured
X-Kvell-Event-Id: evt_5b6c7d8e9f0a
X-Kvell-Delivery: whd_0f1a2b3c4d5e
X-Kvell-Signature: sha256=8f0e1d2c3b4a59687a1e2d3c4b5a6978c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f
X-Request-Id: 3c9e6a1b-7f2d-4e8a-b1c9-0d2f3a4b5c6d{
"eventId": "evt_5b6c7d8e9f0a",
"eventType": "payment.captured",
"occurredAt": "2026-04-13T10:00:05Z",
"merchantId": "mrc_8a1b2c3d4e5f",
"payload": {
"paymentId": "pay_1a2b3c4d5e6f",
"transactionId": "ORDER-2026-4521",
"amount": 120000,
"currency": "TRY",
"status": "captured"
}
}The event carries the order total, not a per-seller breakdown β Kvell tracks each seller's share internally. Fetch GET /v1/payments/{id} if you need to show the split back to the buyer or reconcile it against your own order records.
3. Pay each seller out
Once a seller's share has settled, disburse it with a submerchant payout. The destination IBAN and account holder are sourced from the submerchant's own record β you only pass the amount:
curl -X POST https://api.pay.kvell.group/v1/payouts \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:05:00Z" \
-H "X-Signature: <signature>" \
-H "Content-Type: application/json" \
-d '{
"submerchantId": "sub_7f3a9c21",
"rail": "fast",
"amountMinor": 70000,
"currency": "TRY"
}'{
"payoutId": "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809",
"status": "pending",
"submerchantId": "sub_7f3a9c21"
}Repeat for seller B with amountMinor: 40000. Each payout resolves asynchronously β poll GET /v1/payouts/{id} or listen for payout.succeeded / payout.failed on your webhook endpoint. Testing in the Sandbox covers the payout-success / payout-declined scenarios so you can exercise both outcomes before going live.
Rails available for submerchant payouts
rail accepts the same three values as a regular payout:
| Rail | Destination | Notes |
|---|---|---|
fast | TR IBAN | Instant, 24/7 |
eft | TR IBAN | Interbank, business hours |
card | Card token | Push-to-card (OCT) |
Refunds on a split payment
Refund a split payment the same way you refund any payment β POST /v1/payments/{id}/refund. Kvell reverses the refunded amount proportionally across the original owners (platform and each submerchant), so nobody's share is refunded out of proportion to what they received. A partial refund on the example above reduces each party's held balance in the same ratio the original capture credited it.
Checking available balance before paying out
Before disbursing, confirm what's actually outstanding:
curl https://api.pay.kvell.group/v1/payouts/outstanding \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:00:00Z" \
-H "X-Signature: <signature>"{
"merchantId": "mrc_8a1b2c3d4e5f",
"pendingMinor": 250000,
"currency": "TRY"
}For a large number of sellers, batch the disbursement instead of calling POST /v1/payouts in a loop β see Bulk payouts for many sellers below.
Bulk payouts for many sellers
If you're paying out dozens of submerchants on the same run β a weekly seller settlement, for example β upload a draft instead of issuing payouts one at a time:
curl -X POST https://api.pay.kvell.group/v1/payout-drafts \
-H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
-H "X-Timestamp: 2026-04-13T10:00:00Z" \
-H "X-Signature: <signature>" \
-H "Content-Type: application/json" \
-d '{
"storeId": "sto_5a6b7c8d",
"rows": [
{ "rail": "fast", "dest": "TR330006100519786457841326", "holderName": "Seller A", "amountMinor": 70000, "currency": "TRY" },
{ "rail": "fast", "dest": "TR120006200119000006672315", "holderName": "Seller B", "amountMinor": 40000, "currency": "TRY" }
]
}'{
"draftId": "drf_8192a3b4c5d6",
"status": "draft",
"rowCount": 2,
"validCount": 2,
"invalidCount": 0,
"createdAt": "2026-04-13T10:00:00Z"
}Review the draft with GET /v1/payout-drafts/{id}, then call POST /v1/payout-drafts/{id}/approve β Kvell fans each validated row out to a real payout, at most one per row. A draft accepts up to 1,000 rows, one store per draft, and dest (the IBAN or card token) is never echoed back in any response.
Errors specific to marketplace payouts
| Code | HTTP | Meaning |
|---|---|---|
KVELL-PO-4093 | 422 | Payout exceeds the single-payout or daily limit |
KVELL-PO-4094 | 422 | Insufficient settled balance for this payout |
KVELL-PO-4095 | 409 | Submerchant is not active yet (KYC incomplete or suspended) |
KVELL-PO-4096 | 422 | Submerchant has no bank account on file β resolve this in the dashboard first |
Next steps
| Guide | What it covers |
|---|---|
| Accepting Card Payments | Plain (non-split) hosted checkout, 3DS, refunds |
| Webhooks | Delivering payment.captured and payout.* events reliably |
| Testing in the Sandbox | Scripting the whole split β capture β payout flow risk-free |
| API reference | Full Marketplace and Payouts endpoint docs |