Enterprise platform fee
Every enterprise deal pays one platform fee to RekberPay. This page explains how it is priced, how the two parties split it, and where it shows in the API. Auth, roles, the workspace header and the shared limits are explained in Enterprise overview.
How the fee is priced
platform_fee = max(floor(deal total x rate), minimum), at most deal total minus 1 per milestone- Rate: 2% of the deal total by default.
- Minimum: Rp5.000 per deal by default. The minimum is in rupiah and applies to IDR deals only; a deal in another currency pays the rate with no minimum.
- Cap: the fee never goes above the deal total minus 1 per milestone, so every milestone always pays out at least 1.
- Volume pricing (optional): RekberPay can agree a lower rate once your workspace releases more volume. Each tier says "from this much released in the last 30 days, this rate". The highest tier your workspace reached applies when the deal is created. Rates never go up with volume. Only IDR deals count toward the volume, and sandbox volume only counts toward sandbox deals.
The pricing is the pricing of the workspace that creates the deal (the RekberPay customer). It is set by RekberPay for your workspace and cannot be changed through the API; talk to your account manager about volume pricing.
Who pays: buyer and seller
In an enterprise deal the buyer is your workspace: it funds the milestones. The seller is the deal counterparty: it receives the payouts. Responses name them payer: "workspace" and payee: "counterparty".
The two parties agree who pays the fee per deal:
fee_allocation | fee_buyer_percent | Meaning |
|---|---|---|
buyer | 100 | Your workspace pays the whole fee |
seller | 0 | The counterparty pays the whole fee |
split | 0 to 100 | Your workspace pays this whole percent, the counterparty the rest (0 and 100 work the same as seller and buyer) |
You can send fee_allocation, fee_buyer_percent, or both (they must agree). When a deal is created without either, it takes the workspace default, which the workspace owner sets in the dashboard (Settings, Platform fee).
When a split does not divide evenly, the counterparty share takes the odd rupiah, so buyer_fee + seller_fee is always exactly platform_fee.
What it does to the money
- Your share is added to funding. Each milestone carries a pro rata part of your share (
buyer_fee). The money to send for a milestone isfunding_amount = amount + buyer_fee. - The counterparty share is withheld from payouts. Each milestone carries a pro rata part of it (
seller_fee). The counterparty receivespayout_amount = amount - seller_fee, and the release splits must add up topayout_amount(see Release a milestone). - Pro rata, exact to the rupiah. Each milestone gets
floor(share x milestone amount / deal total)and the last milestone takes the remainder, so the parts always add up to the share. - Sandbox deals show the same fee figures, but no real money moves.
When it is fixed
| Deal stage | What can change |
|---|---|
| Draft | The split (PATCH /v1/enterprise/deals/:id). Milestone edits re-spread the fee using the rate and minimum taken when the deal was created |
| Activated or later | Nothing. The fee is part of the terms the counterparty funds and gets paid against (fee.frozen: true) |
Pricing changes on your workspace never reach deals that already exist. In rare cases RekberPay may change one deal's split at the request of both parties; that change never touches money already funded or withheld, is written to the deal history (deal.fee_allocation_overridden) with its reason, and recomputes only the shares still open.
In deal responses
GET /v1/enterprise/deals/:id and every deal write return deal.fee, and each milestone returns its shares:
{
"deal": {
"total_amount": 150000000,
"fee": {
"rate_bp": 200,
"rate_percent": 2,
"min_fee": 5000,
"tier": null,
"volume_basis": 0,
"platform_fee": 3000000,
"allocation": "split",
"buyer_percent": 50,
"seller_percent": 50,
"buyer_fee": 1500000,
"seller_fee": 1500000,
"funding_total": 151500000,
"payout_total": 148500000,
"payer": "workspace",
"payee": "counterparty",
"frozen": true
}
},
"milestones": [
{ "amount": 50000000, "buyer_fee": 500000, "seller_fee": 500000, "funding_amount": 50500000, "payout_amount": 49500000 },
{ "amount": 100000000, "buyer_fee": 1000000, "seller_fee": 1000000, "funding_amount": 101000000, "payout_amount": 99000000 }
]
}deal.fee is null on a deal created before platform fees existed; such a deal carries no fee. GET /v1/enterprise/deals returns platform_fee per deal (null for those deals), and the deals export adds fee_rate_bp, platform_fee, fee_buyer_percent, buyer_fee and seller_fee columns.
Get the fee terms
Your workspace's pricing, the default split and the rate in force for your current released volume.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role: Any workspace member (
viewerand up). - Rate limit: 300 requests a minute per user, shared by every route.
- Idempotency: Not needed. Reading has no side effects.
Example request
curl https://api.rekberpay.com/v1/enterprise/fees \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"const res = await fetch('https://api.rekberpay.com/v1/enterprise/fees', {
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
},
});
const data = await res.json();import os
import requests
resp = requests.get(
"https://api.rekberpay.com/v1/enterprise/fees",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/fees');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"rate_bp": 200,
"rate_percent": 2,
"min_fee": 5000,
"tiers": [{ "min_volume": 1000000000, "rate_bp": 150, "rate_percent": 1.5 }],
"default_allocation": "buyer",
"default_buyer_percent": 100,
"default_seller_percent": 0,
"uses_platform_rate": true,
"uses_platform_min": true,
"volume_window_days": 30,
"trailing_volume": 1200000000,
"rate_in_force_bp": 150,
"rate_in_force_percent": 1.5,
"tier_in_force": { "min_volume": 1000000000 },
"can_edit_default_allocation": false,
"pricing_set_by": "rekberpay"
}
}Errors
| Status | Code | Cause |
|---|---|---|
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
403 | NO_WORKSPACE | The caller has no active membership in the workspace |
Preview a deal fee
The fee a deal with these milestones would pay if you created it now, with the same calculation as Create a deal. Nothing is saved.
- Auth: A workspace API key (
Authorization: Bearer rpe_...), a personal API key or a dashboard session. - Role: Any workspace member (
viewerand up). - Rate limit: 60 previews a minute per user, plus the shared 300 a minute.
- Idempotency: Not needed. A preview has no side effects.
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
milestones | array | Yes | 1 to 20 items of { "amount": integer }, whole rupiah 10,000 to 50,000,000,000 |
fee_allocation | string | No | buyer, seller or split. Default: the workspace default split |
fee_buyer_percent | integer | No | 0 to 100, your workspace's share. Required with split |
currency | string | No | Three letters, default IDR. The minimum applies to IDR only |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/fees/preview \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Content-Type: application/json" \
-d '{
"milestones": [{ "amount": 50000000 }, { "amount": 100000000 }],
"fee_allocation": "split",
"fee_buyer_percent": 50
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/fees/preview', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
milestones: [{ amount: 50000000 }, { amount: 100000000 }],
fee_allocation: 'split',
fee_buyer_percent: 50,
}),
});
const data = await res.json();import os
import requests
resp = requests.post(
"https://api.rekberpay.com/v1/enterprise/fees/preview",
headers={
"Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
},
json={
"milestones": [{"amount": 50000000}, {"amount": 100000000}],
"fee_allocation": "split",
"fee_buyer_percent": 50,
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/fees/preview');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'milestones' => [['amount' => 50000000], ['amount' => 100000000]],
'fee_allocation' => 'split',
'fee_buyer_percent' => 50,
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"total_amount": 150000000,
"rate_bp": 200,
"rate_percent": 2,
"min_fee": 5000,
"tier": null,
"volume_basis": 0,
"platform_fee": 3000000,
"allocation": "split",
"buyer_percent": 50,
"seller_percent": 50,
"buyer_fee": 1500000,
"seller_fee": 1500000,
"funding_total": 151500000,
"payout_total": 148500000,
"payer": "workspace",
"payee": "counterparty",
"milestones": [
{ "amount": 50000000, "buyer_fee": 500000, "seller_fee": 500000, "funding_amount": 50500000, "payout_amount": 49500000 },
{ "amount": 100000000, "buyer_fee": 1000000, "seller_fee": 1000000, "funding_amount": 101000000, "payout_amount": 99000000 }
],
"is_test": false
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | No milestones, more than 20, an amount out of range, or a percent outside 0 to 100 |
400 | INVALID_FEE_ALLOCATION | split without fee_buyer_percent, or buyer/seller with a percent that disagrees |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
429 | RATE_LIMITED | More than 60 previews a minute |
Set the default split
Sets the split that prefills new deals in your workspace. Existing deals keep the split they were created with.
- Auth: Dashboard session only. API keys, workspace or personal, get
403 KEY_NOT_ALLOWED. - Role:
owneronly. - Rate limit: 30 money writes a minute per user, plus the shared 300 a minute.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
fee_allocation | string | One of the two | buyer, seller or split |
fee_buyer_percent | integer | One of the two | 0 to 100, your workspace's share. Required with split |
Example request
curl -X PUT https://api.rekberpay.com/v1/enterprise/fees/default-allocation \
-b cookies.txt \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H "X-Enterprise-Account: $WORKSPACE_ID" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "fee_allocation": "split", "fee_buyer_percent": 50 }'const res = await fetch('https://api.rekberpay.com/v1/enterprise/fees/default-allocation', {
method: 'PUT',
headers: {
Cookie: process.env.REKBERPAY_SESSION_COOKIE, // '__Host-rekber_session=...' from POST /v1/auth/login
'X-CSRF-Token': process.env.REKBERPAY_CSRF_TOKEN,
'X-Enterprise-Account': process.env.WORKSPACE_ID,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({ fee_allocation: 'split', fee_buyer_percent: 50 }),
});
const data = await res.json();import os
import uuid
import requests
resp = requests.put(
"https://api.rekberpay.com/v1/enterprise/fees/default-allocation",
headers={
"Cookie": os.environ["REKBERPAY_SESSION_COOKIE"],
"X-CSRF-Token": os.environ["REKBERPAY_CSRF_TOKEN"],
"X-Enterprise-Account": os.environ["WORKSPACE_ID"],
"Idempotency-Key": str(uuid.uuid4()),
},
json={"fee_allocation": "split", "fee_buyer_percent": 50},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/fees/default-allocation');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Cookie: ' . getenv('REKBERPAY_SESSION_COOKIE'),
'X-CSRF-Token: ' . getenv('REKBERPAY_CSRF_TOKEN'),
'X-Enterprise-Account: ' . getenv('WORKSPACE_ID'),
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['fee_allocation' => 'split', 'fee_buyer_percent' => 50]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK: the workspace terms, as in Get the fee terms, without the volume fields.
{
"success": true,
"data": {
"rate_bp": 200,
"rate_percent": 2,
"min_fee": 5000,
"tiers": [],
"default_allocation": "split",
"default_buyer_percent": 50,
"default_seller_percent": 50,
"uses_platform_rate": true,
"uses_platform_min": true,
"volume_window_days": 30
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | Neither field sent, or a percent outside 0 to 100 |
400 | INVALID_FEE_ALLOCATION | split without fee_buyer_percent, or buyer/seller with a percent that disagrees |
403 | KEY_NOT_ALLOWED | The request came with an API key |
403 | FORBIDDEN | The caller is not the workspace owner |
Fee errors on other endpoints
| Status | Code | Where |
|---|---|---|
400 | INVALID_FEE_ALLOCATION | Create a deal, update a draft deal |
400 | SPLITS_MISMATCH | Release: the splits do not add up to the milestone payout_amount |
409 | NOT_DRAFT | Update a deal: the split of an activated deal cannot change |