Marketplace with Seller Payouts
The two-sided marketplace pattern: buyers pay the platform, the platform retains a commission, and the remaining amount is paid out to the seller on a recurring cadence, typically weekly or monthly. The hard part is not collecting from buyers; it's getting the seller payouts right at scale, with full audit trails, and surviving the partner disputes that come every quarter.
Real-world examples. Etsy (sellers paid weekly via direct deposit, fees deducted), Shopify Payments (per-transaction fees + monthly subscription, settles daily), Uber and DoorDash (driver/courier paid weekly with per-trip breakdowns), Airbnb (host paid 24h after check-in), Substack (creator paid monthly with platform cut), Patreon, OnlyFans. Common shape: per-transaction commission rate (5-30%), separate platform fees, weekly or monthly payout cycle, partner-facing PDF statements, dispute reconciliation tooling.
The shape of the problem
Two-sided money flow means twice the operational surface:
- Buyer-side billing is "easy" - it's a normal charge. The hard parts are seller-side.
- Seller payout reconciliation. For each period, you must: aggregate every sale the seller was party to, apply the commission, subtract any seller-specific deductions (refunds, chargebacks), and produce both the payout amount and the line-by-line statement that justifies it.
- Who supplies, for tax purposes. Depending on what is sold and where the parties are, either the seller or the platform is the supplier on the buyer's document. Getting this wrong means the wrong party owes the VAT.
- Disputes. Sellers will dispute totals - sometimes monthly. The reconciliation tooling has to let support drill from the payout total down to the individual money movement.
- Holding the money until delivery. The buyer pays first; the seller is credited only once the order is delivered, and an order that is never delivered has to be unwound and refunded.
- Multi-tier commission rules. "10% commission, a lower rate above a sale size, a negotiated rate for a verified Pro seller" - rules layered on top of a base rate.
- Seller-facing transparency. Sellers expect to see pending balances, completed statements, and the per-sale breakdown. Anything less generates support tickets.
Kontier blueprint
Kontier's marketplace is built from your own customers: a seller is a customer that has been given the seller role, a buyer is any other customer, and a sale between them is agreed, paid, completed and then rolled into the seller's periodic statement, against which a payout is prepared.
| Concern | Kontier primitive |
|---|---|
| Seller onboarding and verification | A customer with the seller role: POST /v1/marketplace/sellers, POST /v1/marketplace/sellers/{id}/verify |
| Buyer-side charging | A sale (POST /v1/marketplace/sales) charged with POST /v1/marketplace/sales/{id}/checkout-session |
| Commission | A catalog commission product whose price is a formula over the sale's gross (sale_gross); the fee is frozen onto each sale |
| Who supplies for tax | Decided per sale from what is sold (what) and where the parties are; recorded in the sale's supplied |
| Delivery and crediting the seller | POST /v1/marketplace/sales/{id}/complete |
| Seller payout aggregation | A statement per seller, currency and period, closed automatically on the statement cadence (weekly or monthly) |
| Per-seller dashboard | GET /v1/marketplace/statements?seller_customer_id=…, GET /v1/marketplace/sales?seller_customer_id=… |
| Dispute drill-down | Each statement line names the money movement it came from; GET /v1/marketplace/sales/{id}/postings lists every movement of one sale |
| Partner-facing statement | GET /v1/marketplace/statements/{id}/pdf |
| Recording the bank transfer | POST /v1/marketplace/payouts/{payout_id}/confirm |
Build it
IDs in the examples are placeholders: use the id each create call returns.
1. Switch the marketplace on and set its policy
The marketplace is off for every organization until it is switched on. Its policy lives in the organization settings: set them with PUT /v1/organization/settings.
| Setting | Key | What it decides |
|---|---|---|
| Marketplace enabled | marketplace.enabled | Whether the marketplace surface exists for your organization at all. An organization below another one has it only when its parent has it too |
| Commission product | marketplace.commission_product | The id of the catalog product whose price is the fee on each sale (step 2). A sale is refused until one is set. Each workspace names a product from its own catalog |
| Commission on a refund | marketplace.commission_on_refund | refund (default) gives your fee back in proportion to what the buyer got back; keep retains it |
| Completion window (days) | marketplace.completion_window_days | How long a paid sale has to be completed before it is cancelled and the buyer refunded automatically (1 to 90, default 14) |
| Statement cadence | marketplace.statement_cadence | weekly or monthly (default) |
| Payout threshold | marketplace.payout_threshold | The smallest statement net that is paid out, in whole units of the payout currency (default 0); below it the balance carries into the next period |
| Seller verification | marketplace.seller_verification | none, vat_id (default; a business seller's VAT ID is checked against VIES) or operator (somebody verifies each seller by hand) |
For this blueprint, switch it on with weekly statements and vat_id verification:
Code
Values travel as strings in their wire spelling ("true", "14"). marketplace.enabled and marketplace.seller_verification are set on the organization only, so save them from the live workspace: a sandbox cannot set them. GET /v1/organization/settings/catalog lists every key with its type, default and allowed values.
Commission product, commission on a refund, completion window, statement cadence and payout threshold resolve per seller: to see which value applies to one seller, call GET /v1/organization/settings/resolve?entity=customer:{seller_customer_id}, where each value names the level that supplied it. PUT /v1/organization/settings writes organization and workspace values only.
2. Price your commission as a product
A commission is a catalog product: its price is a formula tier over sale_gross, the sale's agreed gross, which the price resolver binds when it prices a sale's fee. The fee is priced at quantity 1 (one sale, one fee), and rate_expression takes precedence over unit_amount.
Code
Then name this product as the commission product:
Code
A fixed fee plus a percentage is 2 + sale_gross * 0.05. A product whose price reads sale_gross can only be priced for a marketplace sale; resolving it anywhere else is refused.
3. Onboard the seller
A seller is first a customer: its name, address and tax identifiers live on the customer. Then give it the seller role with its document-number prefix and the bank account it is paid into:
Code
The seller's country is taken from the customer's billing address when you omit it. The verification check your setting asks for runs right away, so the seller may come back verified or still pending (statuses: pending, verified, suspended, closed). Under operator verification, verify it once you have checked it:
Code
marketplace.seller.created and marketplace.seller.verified fire as the seller moves through these steps. Suspend a seller with POST /v1/marketplace/sellers/{id}/suspend (its paid sales still complete and its statements still run) and lift it with POST /v1/marketplace/sellers/{id}/reinstate.
4. Agree a sale and charge the buyer
The buyer is an ordinary customer. Agreeing a sale works out who supplies for tax purposes and prices your fee, and freezes both onto the sale:
Code
listing_reference is your storefront's id for what is sold; Kontier stores it and never interprets it. what is one of ESS (a digital service delivered electronically), OTHER_SERVICES, RIGHTS or GOODS; who supplies (AGENT or DEEMED_SUPPLIER) is decided from it and cannot be chosen. The sale starts agreed. Charge the buyer's saved payment method:
Code
What comes back is the payment to follow, not the sale. When the payment goes through, the sale becomes funded, its complete_by deadline is set from the completion window, and marketplace.sale.funded fires.
5. Complete the sale on delivery
Code
Completing credits the seller with the sale less your fee, issues the buyer's document in the name of whoever supplied, and invoices the seller for the fee, all or nothing (marketplace.sale.completed). A funded sale that is not completed by complete_by is cancelled automatically and the buyer refunded. Before delivery you can cancel it yourself with POST /v1/marketplace/sales/{id}/cancel.
6. Statements close on the cadence
There is nothing to schedule: a daily pass closes every seller period that has ended (weekly for this blueprint) into a statement, one per seller, currency and period. A statement is numbered, frozen and sent to the seller, and marketplace.statement.finalized fires. List a seller's statements and read one:
Code
Code
Line amounts are signed as the seller reads them: a sale adds; a fee, a refund and a dispute take away. period.ends is the first instant after the period. The seller-facing PDF is GET /v1/marketplace/statements/{id}/pdf.
7. Pay out and record the transfer
When a statement's net reaches the payout threshold, a payout is prepared against it. Kontier does not move the money: you make the bank transfer, then record it with the bank's own reference:
Code
This clears what the seller was owed, marks the statement paid and fires marketplace.payout.paid. A payout that is already paid is refused rather than paid twice.
Variations
- Tiered commission rates. Express the tiers inside the commission price's expression over
sale_gross, e.g.if(sale_gross > 1000, sale_gross * 0.05, sale_gross * 0.10), orbracket(sale_gross, SLUG)against a rate table. The fee is priced at quantity 1, so the quantity ladder does not express it. For a seller with a negotiated rate, keep a separate commission product and pass it ascommission_product_idwhen you agree each of that seller's sales. - Per-category commissions. Keep one commission product per category and pass the right one as
commission_product_idwhen you agree the sale. - Multi-currency sales. Each sale is in its own currency, and a seller gets one statement per currency and period; amounts are not converted between currencies on a statement.
- Refund and chargeback handling.
POST /v1/marketplace/sales/{id}/refundgives the buyer back all or part of a delivered sale, takes it out of what the seller is owed and issues a credit note; your fee follows it back or is kept per the "commission on a refund" setting frozen on the sale. A lost dispute is borne by the seller or by you; move it withPOST /v1/marketplace/sales/{id}/chargeback-bearer. - Holdbacks and tax withholding. Statements have
RESERVEandADJUSTMENTline kinds, but there is no rule today that withholds a chargeback reserve or tax from a seller's payout automatically; use the payout threshold to hold small balances back. - Platform balances.
GET /v1/marketplace/ledger/balancesshows, per currency, the buyers' money you hold, what each seller is owed, your fees and disputes you absorbed, with a cross-check that is zero when the books balance.
What you don't have to build
- Holding the buyer's money until delivery, with automatic cancel-and-refund after the completion window
- Deciding who supplies for tax purposes and issuing the buyer's document in that party's name
- Commission math (a catalog price over
sale_gross, frozen onto each sale) - Per-seller, per-period aggregation into numbered statements (closed automatically on the cadence)
- Refund clawback against the seller's balance, with the fee returned or kept per your policy
- Partner-facing PDF statement (
/marketplace/statements/{id}/pdf) - Pending balances per seller (
/marketplace/payouts?status=prepared&seller_customer_id=…) - Audit trail for every dispute (each statement line names the movement it came from)
Next steps
- Marketplace API - sellers, sales, statements and payouts
- Credit Notes - refund documents
- Formulas - expressions for the commission price
- Webhook events - the
marketplace.*events