Skip to content

Platform mode (PaaS) ​

The partner API for marketplaces. You create and link users, open marketplace deals, send buyers to checkout, report shipping, follow disputes and pay out balances. Start with Platform mode (PaaS) to see which part does what.

Basics ​

  • Base URL: https://api.rekberpay.com/v1/enterprise/platform. Sandbox and live use the same URL; the key decides the workspace and its mode (rpe_test_ for Sandbox, rpe_live_ for Live). Every read and write stays in that mode.
  • Platform must be on in the request mode. Each mode has its own switch and freeze. A mode RekberPay switched off gets 404 NOT_FOUND on every route here, and a frozen mode gets 423 PLATFORM_FROZEN. Live needs verified KYB (403 KYB_REQUIRED); Sandbox does not. Read both modes' state with GET /v1/enterprise/platform-status.
  • Credentials. A workspace key (rpe_test_ or rpe_live_) from your server, or a dashboard session. A personal key or an app session gets 403 KEY_NOT_ALLOWED. A key sent with a browser Origin or Sec-Fetch-Site header gets 403 KEY_IN_BROWSER. See Keep your keys safe.
  • Scopes. Each route names its Platform scope. A key needs the scope on the key, the scope in your allowed_scopes, and write for any change. The member who made the key must still hold a role that allows it: operator or higher for users, orders, kyc and ratings; finance or higher for orders:confirm, balance:read and withdraw.
  • Idempotency. Every POST, PATCH and DELETE from a key needs an Idempotency-Key (UUID v4). Answers are kept for 24 hours; only 2xx answers are kept, so a refused call can be retried with the same key.
  • Users are refs. You name people by your partner_user_ref. Internal user ids never appear in answers or webhooks.
  • Out of scope is 404. Another partner's data, staff accounts and users who have not linked to you answer 404 NOT_FOUND. While a link is pending_consent or revoked, every call about that ref answers 404 except Get a user and Create or link a user.
  • Money is whole rupiah. Bank numbers are always masked.
  • Sandbox. No email is sent and no gateway is charged. Links that an email would carry come back in the answer (connect_url, email_change_url, claim_url, release_notice_url). Those answers are kept in the idempotency store for 24 hours like any other answer.

Endpoints ​

MethodPathEndpointScope
GET/v1/enterprise/platform/settingsGet your Platform settingsnone
POST/v1/enterprise/platform/usersCreate or link a userusers
GET/v1/enterprise/platform/users/:refGet a userusers
PATCH/v1/enterprise/platform/users/:refUpdate a userusers
DELETE/v1/enterprise/platform/users/:refUnlink a userusers
POST/v1/enterprise/platform/users/:ref/claim-linkCreate a claim linkusers
POST/v1/enterprise/platform/users/:ref/kycSubmit KYCkyc
GET/v1/enterprise/platform/users/:ref/kycGet KYC statuskyc
POST/v1/enterprise/platform/users/:ref/kyc/simulateSimulate a KYC result (sandbox)kyc
POST/v1/enterprise/platform/dealsCreate a dealorders
GET/v1/enterprise/platform/deals/:idGet a dealorders
GET/v1/enterprise/platform/dealsList dealsorders
POST/v1/enterprise/platform/deals/:id/cancelCancel a dealorders
POST/v1/enterprise/platform/deals/:id/milestones/:mid/checkoutGet a checkout linkorders
POST/v1/enterprise/platform/deals/:id/milestones/:mid/cancelCancel a milestoneorders
POST/v1/enterprise/platform/deals/:id/milestones/:mid/shipShip a milestoneorders
POST/v1/enterprise/platform/deals/:id/milestones/:mid/confirmConfirm receiptorders:confirm
POST/v1/enterprise/platform/deals/:id/milestones/:mid/extendExtend the auto releaseorders
POST/v1/enterprise/platform/deals/:id/milestones/:mid/disputeOpen a disputeorders
POST/v1/enterprise/platform/deals/:id/milestones/:mid/dispute/evidenceAdd dispute evidenceorders
GET/v1/enterprise/platform/users/:ref/balanceRead a balancebalance:read
GET/v1/enterprise/platform/users/:ref/bank-accountsList bank accountswithdraw
POST/v1/enterprise/platform/users/:ref/bank-accountsAdd a bank accountwithdraw
POST/v1/enterprise/platform/users/:ref/withdrawalsRequest a withdrawalwithdraw
GET/v1/enterprise/platform/users/:ref/withdrawals/:idGet a withdrawalwithdraw
POST/v1/enterprise/platform/deals/:id/ratingsRate a completed dealratings
GET/v1/enterprise/platform/deals/:id/ratingsGet a deal's ratingsratings
GET/v1/enterprise/platform/users/:ref/reputationGet a user's reputationusers
PUT/v1/enterprise/platform/settings/profile-visibilityShow unclaimed profiles (dashboard)none, dashboard only

Sandbox shortcuts ​

These work only in Sandbox and answer 404 in Live. They let you run a whole order in minutes instead of days.

CallScopeWhat it does
POST /v1/platform/checkout/:token/simulatenone, the checkout tokenPays a milestone with test money
POST /v1/enterprise/platform/users/:ref/kyc/simulatekycApproves or rejects a pending KYC
POST /v1/enterprise/platform/deals/:id/milestones/:mid/simulate/advance-timeordersReleases a shipped or release_pending milestone now, as if its time ran out
POST /v1/enterprise/platform/users/:ref/bank-accounts/:id/simulate/skip-cooling-offwithdrawEnds a payout bank account's cooling-off now

Rate limits ​

Platform budgets are per key (per member for a dashboard session). On top of them, every call counts against the shared 300 requests a minute of the member who made the key, so all keys made by one member, and that member's dashboard session, share those 300. For high volume, make keys from different members.

RoutesBudget
Reads: settings, users, KYC status, deals, balances, bank accounts, withdrawals, ratings, reputation120 a minute
Deal and milestone writes: create, cancel, checkout, ship, extend, dispute, evidence, ratings, profile visibility60 a minute
User writes: create, update, unlink, claim links, KYC submit and simulate30 a minute
Money: confirm, add a bank account, request a withdrawal, and the two sandbox time shortcuts30 a minute

Over budget you get 429 RATE_LIMITED with a Retry-After header.

Errors every Platform route can answer ​

StatusCodeCauseWhat to do
400IDEMPOTENCY_KEY_REQUIREDA key write without Idempotency-KeySend a UUID v4, and reuse it on retries
400IDEMPOTENCY_KEY_INVALIDThe key is not a UUID v4Send a UUID v4
401UNAUTHORIZED, INVALID_TOKENNo credential, a wrong or revoked key, or a checkout, claim or connect token sent as a keySend a valid workspace key
401KEY_IP_NOT_ALLOWEDThe key has an IP allowlist and this address is not on itCall from an allowed address
403KEY_IN_BROWSERA workspace key sent from a browser (Origin or Sec-Fetch-Site header)Call from your server only
403KEY_NOT_ALLOWEDA personal API key, an app device session or a WhatsApp sessionUse a workspace key or the dashboard
403SCOPE_REQUIREDThe key lacks the scope, your terms do not allow it, or a read-only key tried to writeMake a key with the scope; check allowed_scopes
403FORBIDDENThe member who made the key no longer has a role that allows the scopeMake a new key from a member with the right role
403KYB_REQUIREDA Live request of a workspace without verified KYBFinish business verification
403LIVE_LOCKEDA Live key while the workspace's Live is lockedWork in Sandbox until Live unlocks
403KEY_MODE_MISMATCHX-Enterprise-Mode names the other mode than the key'sRemove the header, or use a key of that mode
403IP_ALLOWLIST_REQUIREDA Live key with a Platform scope whose IP allowlist RekberPay has not approved yetSend your server IPs on the key in the dashboard and wait for approval (going live)
404NOT_FOUNDRekberPay switched Platform off for your workspace in this mode, or the object is not in your scope (another partner or the other mode)Check the id or ref
409CONCURRENT_MODIFICATIONTwo writes raced and this one was rolled back whole. Nothing changedRead again and retry, also with the same Idempotency-Key
409IDEMPOTENCY_KEY_IN_FLIGHTThe first call with this key is still runningWait, then retry
422IDEMPOTENCY_KEY_CONFLICTThe same key was used with another JSON bodyUse a new key for a new request
423PLATFORM_FROZENRekberPay paused your Platform accessContact your account manager
429RATE_LIMITEDOver a budgetWait for Retry-After seconds

Get your Platform settings ​

GET/v1/enterprise/platform/settings

Your Platform terms as RekberPay set them: the fee, the default fee payer, the daily cap, the confirm delay and the scopes your keys may hold. Platform is on for every workspace with the standard terms until RekberPay changes them. If RekberPay switched Platform off for your workspace this call answers 404, and while RekberPay has frozen your access it answers 423 PLATFORM_FROZEN, so a successful answer always shows enabled: true and frozen: false.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: None. Any key or member of the workspace may read it.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/settings" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/settings', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/settings",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/settings');
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

json
{
    "success": true,
    "data": {
        "enabled": true,
        "frozen": false,
        "fee_rate_bps": 200,
        "fee_min_idr": 5000,
        "default_fee_bearer": "buyer",
        "default_split_buyer_pct": 50,
        "daily_cap_idr": 50000000,
        "confirm_delay_hours": 6,
        "allowed_scopes": [
            "users",
            "kyc",
            "orders",
            "balance:read",
            "withdraw",
            "ratings"
        ],
        "show_unclaimed_profiles": false,
        "show_unclaimed_consent_at": null,
        "show_unclaimed_consent_version": null,
        "show_unclaimed_changed_by": null
    }
}
FieldMeaning
fee_rate_bps, fee_min_idrThe RekberPay fee per deal: 200 basis points is 2 percent, with this minimum in rupiah
default_fee_bearer, default_split_buyer_pctWho pays the fee when a deal does not say: buyer, seller or split (the buyer's percent)
daily_cap_idrMost new deal value per WIB day, or null for no cap. Cancelled deals do not count
confirm_delay_hoursHow long a release waits after your confirm
allowed_scopesThe Platform scopes a key of yours may hold
show_unclaimed_profilesWhether accounts you created that nobody claimed yet have a public profile. Off by default; see Show unclaimed profiles
show_unclaimed_consent_at, show_unclaimed_consent_versionWhen the switch was last changed, and the consent statement version ticked when it was turned on (null while it is off, or after RekberPay changed it)
show_unclaimed_changed_byWho last set it: a member's name, or RekberPay

Errors ​

StatusCodeCause
Plus the errors every Platform route can answer
POST/v1/enterprise/platform/users

Creates a RekberPay account for a new email and links it to you at once, or asks a person who already has an account to link (pending_consent). See Create and link users.

CaseStatuslink_status
New email: account created, unclaimed, no password201active
Known email: a confirmation email goes to the address on file201pending_consent
A revoked link of this person: reopened, the person confirms again201pending_consent
The same ref and email again (a retry). A pending link is emailed again, at most once per 10 minutes200unchanged

A RekberPay staff account, or a suspended or closing one, is handled exactly like any known email: the same pending_consent request, the same answers to a repeat POST and to GET, the same email limits. The request is simply never emailed and can never be accepted, so it stays pending_consent. Nothing tells you which accounts are staff or blocked.

Live email limits, so a partner can never use RekberPay to flood someone's inbox:

  • A link gets at most 3 confirmation emails a day. You have one link per person, so in practice you can send one person 3 a day, and other partners never use up your limit. One address gets at most 20 a day from all partners together. Calls refused past your own limit do not count toward the shared 20. Past any limit the call answers 429 MAIL_LIMIT_REACHED with a Retry-After header (seconds until 00:00 UTC). A new request refused this way is not stored.

  • When the person declined your request, or removed you from their connected partners, you cannot ask again for 7 days: 429 USER_DECLINED_RECENTLY with Retry-After.

  • The sandbox sends no email, so these limits do not apply there.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.

  • Scope: users, and write on the key.

  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.

  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Body (JSON) ​

NameTypeRequiredDescription
partner_user_refstringYesYour id for this person: 1 to 128 letters, digits or _ . : @ + = -
emailstringYesAt most 254 characters. In the sandbox it must be on example.com (or a subdomain), example.test, or a .test or .example domain
full_namestringYes1 to 120 characters, without < or >. Ignored for a known email
phonestring | nullNo6 to 32 digits, spaces, +, -, dots or brackets

In the sandbox no email is sent: a pending_consent answer adds connect_url, the link the person would get.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 6f1d2c3b-4a5e-4f60-8a7b-1c2d3e4f5a60" \
  -H "Content-Type: application/json" \
  -d '{
    "partner_user_ref": "seller-2001",
    "email": "[email protected]",
    "full_name": "Budi Santoso",
    "phone": "+6281234567890"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '6f1d2c3b-4a5e-4f60-8a7b-1c2d3e4f5a60',
    },
    body: JSON.stringify({
        partner_user_ref: "seller-2001",
        email: "[email protected]",
        full_name: "Budi Santoso",
        phone: "+6281234567890",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "6f1d2c3b-4a5e-4f60-8a7b-1c2d3e4f5a60",
    },
    json={
        "partner_user_ref": "seller-2001",
        "email": "[email protected]",
        "full_name": "Budi Santoso",
        "phone": "+6281234567890",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 6f1d2c3b-4a5e-4f60-8a7b-1c2d3e4f5a60',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'partner_user_ref' => 'seller-2001',
        'email' => '[email protected]',
        'full_name' => 'Budi Santoso',
        'phone' => '+6281234567890',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "7c1e2d3f-4a5b-4c6d-8e7f-901a2b3c4d5e",
        "partner_user_ref": "seller-2001",
        "link_status": "active",
        "origin": "created",
        "claimed": false,
        "kyc_status": "none",
        "balance_scope": "partner_funds",
        "limits": {
            "capped": true,
            "kyc_status": "none",
            "per_escrow_max": 10000000,
            "daily_max": 25,
            "lifetime_max": 25000000,
            "max_order_now": 10000000
        },
        "email_masked": "b***@example.com"
    }
}
FieldMeaning
idThe link id
link_statuspending_consent, active or revoked
origincreated (you made the account) or linked (the person had one)
claimedtrue when the person controls the account: always for a linked account, and once a created account is claimed. null unless the link is active
kyc_statusnone, pending, verified or rejected. null unless the link is active
balance_scopepartner_funds, or full_balance when the person let you manage their whole balance
limitsThe non-KYC limits: per_escrow_max rupiah per order, daily_max orders per 24 hours, lifetime_max rupiah in total, and max_order_now, the largest order total that fits right now (0 when the person used up their 24 hour order count). The person's own usage is never shown, because it includes their activity outside your platform. { "capped": false } once verified. null unless the link is active
email_maskedThe email on file, masked. null unless the link is active

Errors ​

StatusCodeCause
400INVALID_BODYA field is missing or not valid
409REF_IN_USEThis partner_user_ref is linked to a different email
409USER_ALREADY_LINKEDThis person is already linked to you under another ref
422USER_NOT_AVAILABLERekberPay ended your link with this person, so it cannot be reopened. They can contact RekberPay support
422SANDBOX_EMAIL_REQUIREDA Sandbox key sent an email outside the test domains
429MAIL_LIMIT_REACHEDLive only: this link or this address already got as many emails as allowed today. Wait Retry-After seconds
429USER_DECLINED_RECENTLYLive only: the person declined or ended the link less than 7 days ago. Wait Retry-After seconds
Plus the errors every Platform route can answer

Get a user ​

GET/v1/enterprise/platform/users/:ref

Reads one link in any status. Personal fields are filled only while the link is active.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: users.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001');
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

json
{
    "success": true,
    "data": {
        "id": "7c1e2d3f-4a5b-4c6d-8e7f-901a2b3c4d5e",
        "partner_user_ref": "seller-2001",
        "link_status": "active",
        "origin": "created",
        "claimed": false,
        "kyc_status": "none",
        "balance_scope": "partner_funds",
        "limits": {
            "capped": true,
            "kyc_status": "none",
            "per_escrow_max": 10000000,
            "daily_max": 25,
            "lifetime_max": 25000000,
            "max_order_now": 10000000
        },
        "email_masked": "b***@example.com"
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo link with this ref in your workspace
Plus the errors every Platform route can answer

Update a user ​

PATCH/v1/enterprise/platform/users/:ref

Changes the name, phone or email of an account you created that is not claimed yet. Name and phone change at once. An email change sends a confirmation link (24 hours) to the new address and applies only when it is opened. In live mode a user gets at most 3 email changes a day, you can send one new address at most 3 confirmation emails a day, and that address gets at most 10 a day from all partners together (429 MAIL_LIMIT_REACHED with Retry-After).

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: users, and write on the key.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Body (JSON) ​

NameTypeRequiredDescription
full_namestringNo1 to 120 characters. Refused once the name is locked (KYC approved, or a balance, a bank account or a paid order with any partner)
phonestring | nullNoAs on create. null removes it
emailstringNoThe new address. Answers "email_change": "verification_sent" whether or not the address is free

Send at least one field. In the sandbox an email change answer adds email_change_url instead of sending the email.

Example request ​

bash
curl -X PATCH "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 0a1b2c3d-4e5f-4a6b-8c7d-8e9f0a1b2c3d" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+6281299998888",
    "email": "[email protected]"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001', {
    method: 'PATCH',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '0a1b2c3d-4e5f-4a6b-8c7d-8e9f0a1b2c3d',
    },
    body: JSON.stringify({
        phone: "+6281299998888",
        email: "[email protected]",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.patch(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "0a1b2c3d-4e5f-4a6b-8c7d-8e9f0a1b2c3d",
    },
    json={
        "phone": "+6281299998888",
        "email": "[email protected]",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 0a1b2c3d-4e5f-4a6b-8c7d-8e9f0a1b2c3d',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'phone' => '+6281299998888',
        'email' => '[email protected]',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "7c1e2d3f-4a5b-4c6d-8e7f-901a2b3c4d5e",
        "partner_user_ref": "seller-2001",
        "link_status": "active",
        "origin": "created",
        "claimed": false,
        "kyc_status": "none",
        "balance_scope": "partner_funds",
        "limits": {
            "capped": true,
            "kyc_status": "none",
            "per_escrow_max": 10000000,
            "daily_max": 25,
            "lifetime_max": 25000000,
            "max_order_now": 10000000
        },
        "email_masked": "b***@example.com",
        "email_change": "verification_sent"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYNo field sent, or a field is not valid
404NOT_FOUNDNo active link with this ref
409USER_MANAGED_BY_OWNERThe person claimed the account, or it was theirs before they linked
409NAME_LOCKEDThe name is locked: KYC approved, or a partner balance, a payout bank account or a paid order exists
422SANDBOX_EMAIL_REQUIREDA Sandbox key sent a new email outside the test domains
429MAIL_LIMIT_REACHEDLive only: email changes for this user, or your confirmations to this new address, reached 3 today, or that address reached 10 today from all partners. Nothing in the request was saved. Wait Retry-After seconds
Plus the errors every Platform route can answer
DELETE/v1/enterprise/platform/users/:ref

Revokes the link. The account and its money stay with the person. To link again, send Create or link a user with the same email; the person must confirm again.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: users, and write on the key.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Example request ​

bash
curl -X DELETE "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 1b2c3d4e-5f60-4b7c-8d8e-9f0a1b2c3d4e"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001', {
    method: 'DELETE',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': '1b2c3d4e-5f60-4b7c-8d8e-9f0a1b2c3d4e',
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.delete(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "1b2c3d4e-5f60-4b7c-8d8e-9f0a1b2c3d4e",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: 1b2c3d4e-5f60-4b7c-8d8e-9f0a1b2c3d4e',
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "7c1e2d3f-4a5b-4c6d-8e7f-901a2b3c4d5e",
        "partner_user_ref": "seller-2001",
        "link_status": "revoked",
        "origin": "created",
        "claimed": null,
        "kyc_status": null,
        "balance_scope": "partner_funds",
        "limits": null,
        "email_masked": null
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo active link with this ref, or the ref is not yours
Plus the errors every Platform route can answer
POST/v1/enterprise/platform/users/:ref/claim-link

Lets the person take over an account you created. Either way they still need the 6 digit code RekberPay emails to the address on file.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: users, and write on the key.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Body (JSON) ​

NameTypeRequiredDescription
deliverystringYesemail: RekberPay emails a 24 hour link (202, no link in the answer; at most one email per 2 minutes and 10 a day per account, the same answer when throttled). link: you get a single use 30 minute link (200)

In the sandbox no email is sent: email answers 202 with a 30 minute claim_url.

A sandbox claim is a simulation. The claim page shows the code instead of emailing it, and finishing it answers { "claimed": true, "sandbox": true }. From then on the user reads claimed: true and you can no longer change the account, as after a real claim. But no password is kept and no session opens: a sandbox test account can never sign in to RekberPay, and a live link can never claim it later. A sandbox link you reopened after an unlink is claimed and confirmed in one step through its connect_url.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/claim-link" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 2c3d4e5f-6071-4c8d-9e9f-0a1b2c3d4e5f" \
  -H "Content-Type: application/json" \
  -d '{
    "delivery": "link"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/claim-link', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '2c3d4e5f-6071-4c8d-9e9f-0a1b2c3d4e5f',
    },
    body: JSON.stringify({
        delivery: "link",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/claim-link",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "2c3d4e5f-6071-4c8d-9e9f-0a1b2c3d4e5f",
    },
    json={
        "delivery": "link",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/claim-link');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 2c3d4e5f-6071-4c8d-9e9f-0a1b2c3d4e5f',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'delivery' => 'link',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "url": "https://rekberpay.com/claim/rpt.eyJwIjoiY2xhaW0iLCJ4IjoxfQ.Hk2mQ9vT4xR7bN1cW8pL5sD0fJ3gA6eY2uI9oZ4tK1q",
        "expires_at": "2026-10-21T08:30:00.000Z"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYdelivery is missing or not email or link
404NOT_FOUNDNo active link with this ref, or the ref is not yours
409USER_MANAGED_BY_OWNERThe account is already claimed, or was the person's own before they linked
Plus the errors every Platform route can answer

Submit KYC ​

POST/v1/enterprise/platform/users/:ref/kyc

Submits the identity check of an account you created that is not claimed yet, as multipart/form-data. RekberPay's team reviews it. See KYC through the API.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: kyc, and write on the key.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required. The key is bound to the path, not to the form: a reused key replays the first answer and the new form is not processed. Use a new key for every upload.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Body (multipart/form-data) ​

NameTypeRequiredDescription
kyc_id_typetextYesktp, sim or passport
kyc_full_name_ktptextYesThe name on the ID, up to 120 characters. Must match a locked name
kyc_niktextYesThe 16 digit NIK. In the sandbox a test NIK that ends in 0000
kyc_entity_nametextNoA foundation or company name, up to 180 characters
kyc_whatsapptextYes08... or +62..., 8 to 20 characters
kyc_provincetextYesOne of the 38 province names, for example DKI Jakarta
kyc_citytextYesUp to 120 characters
kyc_districttextYesUp to 120 characters
kyc_subdistricttextYesUp to 120 characters
kyc_street_addresstextYesUp to 500 characters
kyc_bank_nametextYesA bank or e-wallet code such as bca, mandiri, bri or gopay (the full list is in the KYC guide)
kyc_account_numbertextYes4 to 40 digits
frontfileYesThe ID card. JPG, PNG or WebP, up to 10 MB
selfiefileYesThe person holding the ID. JPG, PNG or WebP, up to 10 MB

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 3d4e5f60-7182-4d9e-8fa0-1b2c3d4e5f60" \
  -F "kyc_id_type=ktp" \
  -F "kyc_full_name_ktp=Budi Santoso" \
  -F "kyc_nik=3171014501900000" \
  -F "kyc_whatsapp=081234567890" \
  -F "kyc_province=DKI Jakarta" \
  -F "kyc_city=Jakarta Selatan" \
  -F "kyc_district=Kebayoran Baru" \
  -F "kyc_subdistrict=Senayan" \
  -F "kyc_street_address=Jl. Asia Afrika No. 8" \
  -F "kyc_bank_name=bca" \
  -F "kyc_account_number=1234567890" \
  -F "[email protected]" \
  -F "[email protected]"
js
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.append("kyc_id_type", "ktp");
form.append("kyc_full_name_ktp", "Budi Santoso");
form.append("kyc_nik", "3171014501900000");
form.append("kyc_whatsapp", "081234567890");
form.append("kyc_province", "DKI Jakarta");
form.append("kyc_city", "Jakarta Selatan");
form.append("kyc_district", "Kebayoran Baru");
form.append("kyc_subdistrict", "Senayan");
form.append("kyc_street_address", "Jl. Asia Afrika No. 8");
form.append("kyc_bank_name", "bca");
form.append("kyc_account_number", "1234567890");
form.append("front", new Blob([await readFile("test-ktp.jpg")], { type: "image/jpeg" }), "test-ktp.jpg");
form.append("selfie", new Blob([await readFile("test-selfie.jpg")], { type: "image/jpeg" }), "test-selfie.jpg");

const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': '3d4e5f60-7182-4d9e-8fa0-1b2c3d4e5f60',
    },
    body: form,
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "3d4e5f60-7182-4d9e-8fa0-1b2c3d4e5f60",
    },
    data={
        "kyc_id_type": "ktp",
        "kyc_full_name_ktp": "Budi Santoso",
        "kyc_nik": "3171014501900000",
        "kyc_whatsapp": "081234567890",
        "kyc_province": "DKI Jakarta",
        "kyc_city": "Jakarta Selatan",
        "kyc_district": "Kebayoran Baru",
        "kyc_subdistrict": "Senayan",
        "kyc_street_address": "Jl. Asia Afrika No. 8",
        "kyc_bank_name": "bca",
        "kyc_account_number": "1234567890",
    },
    files=[
        ("front", ("test-ktp.jpg", open("test-ktp.jpg", "rb"), "image/jpeg")),
        ("selfie", ("test-selfie.jpg", open("test-selfie.jpg", "rb"), "image/jpeg")),
    ],
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: 3d4e5f60-7182-4d9e-8fa0-1b2c3d4e5f60',
    ],
    CURLOPT_POSTFIELDS => [
        'kyc_id_type' => 'ktp',
        'kyc_full_name_ktp' => 'Budi Santoso',
        'kyc_nik' => '3171014501900000',
        'kyc_whatsapp' => '081234567890',
        'kyc_province' => 'DKI Jakarta',
        'kyc_city' => 'Jakarta Selatan',
        'kyc_district' => 'Kebayoran Baru',
        'kyc_subdistrict' => 'Senayan',
        'kyc_street_address' => 'Jl. Asia Afrika No. 8',
        'kyc_bank_name' => 'bca',
        'kyc_account_number' => '1234567890',
        'front' => new CURLFile('test-ktp.jpg', 'image/jpeg'),
        'selfie' => new CURLFile('test-selfie.jpg', 'image/jpeg'),
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

202 Accepted

json
{
    "success": true,
    "data": {
        "partner_user_ref": "seller-2001",
        "kyc_status": "pending"
    }
}

You get platform.user.kyc_updated when the review ends.

Errors ​

StatusCodeCause
400INVALID_BODYNot multipart, a photo is missing, or a field is not valid. details.fields names the fields, never their values
400INVALID_NIKkyc_nik is not a valid NIK
404NOT_FOUNDNo active link with this ref, or the ref is not yours
409USER_MANAGED_BY_OWNERThe person claimed the account, or it was theirs before they linked
409KYC_LOCKEDA KYC is already pending or verified
409NIK_IN_USEThis NIK already verifies another RekberPay account
413FILE_TOO_LARGEA photo is over 10 MB
400TYPE_NOT_ALLOWEDA photo is not JPG, PNG or WebP
400TYPE_MISMATCHThe content of a photo does not match its type or extension
400IMAGE_DECODE_FAILEDA photo is damaged or not a real image
429QUOTA_EXCEEDEDThe person reached the daily upload limit for KYC photos
500STORAGE_FAILEDThe photo could not be stored. Nothing was submitted. Retry with a new Idempotency-Key
422NAME_MISMATCHThe name is locked and kyc_full_name_ktp does not match it
422SANDBOX_TEST_NIK_REQUIREDA sandbox KYC must use a test NIK that ends in 0000
422USER_NOT_AVAILABLEThe account is suspended or closing
429KYC_SUBMIT_LIMITEDToo many submissions for this person or your platform. Try again tomorrow
Plus the errors every Platform route can answer

Get KYC status ​

GET/v1/enterprise/platform/users/:ref/kyc

The KYC status of an active link. Never the NIK, the name, the address, the bank or the photos.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: kyc.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc');
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

json
{
    "success": true,
    "data": {
        "kyc_status": "verified",
        "submitted_at": "2026-10-21T08:00:00.000Z",
        "reviewed_at": "2026-10-21T10:15:00.000Z",
        "rejection_reason": null
    }
}

rejection_reason is filled only for a rejected KYC of an account you manage.

Errors ​

StatusCodeCause
404NOT_FOUNDNo active link with this ref, or the ref is not yours
Plus the errors every Platform route can answer

Simulate a KYC result (sandbox) ​

POST/v1/enterprise/platform/users/:ref/kyc/simulate

Settles a pending sandbox KYC without RekberPay staff and without email, and sends platform.user.kyc_updated. Answers 404 in live mode.

A sandbox KYC is a simulation of your test user only. It is kept apart from the RekberPay account itself, so an approval never makes the account KYC verified on RekberPay and never changes its name. No photo from a sandbox submit is stored.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: kyc, and write on the key.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Body (JSON) ​

NameTypeRequiredDescription
resultstringYesapproved or rejected
reasonstringNo3 to 500 characters, without < or >. Shown as rejection_reason

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc/simulate" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 4e5f6071-8293-4eaf-90b1-2c3d4e5f6071" \
  -H "Content-Type: application/json" \
  -d '{
    "result": "approved"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc/simulate', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '4e5f6071-8293-4eaf-90b1-2c3d4e5f6071',
    },
    body: JSON.stringify({
        result: "approved",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc/simulate",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "4e5f6071-8293-4eaf-90b1-2c3d4e5f6071",
    },
    json={
        "result": "approved",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/kyc/simulate');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 4e5f6071-8293-4eaf-90b1-2c3d4e5f6071',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'result' => 'approved',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "kyc_status": "verified",
        "submitted_at": "2026-10-21T08:00:00.000Z",
        "reviewed_at": "2026-10-21T08:05:00.000Z",
        "rejection_reason": null
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYresult is missing or not valid
404NOT_FOUNDA Live request, or no active link with this ref
409KYC_NOT_PENDINGThere is no pending KYC to settle
422NAME_MISMATCHThe name is locked and the KYC name does not match it
Plus the errors every Platform route can answer

Create a deal ​

POST/v1/enterprise/platform/deals

Opens a marketplace order between two active users of yours, with one or more milestones. Every milestone starts awaiting_payment. The fee is one fee per deal, spread over the milestones.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Body (JSON) ​

NameTypeRequiredDescription
buyer_refstringYesThe buyer's partner_user_ref. Must be an active link
seller_refstringYesThe seller's partner_user_ref. Another person than the buyer
titlestringYes2 to 180 characters
partner_order_refstringYesYour order id, 1 to 128 characters. Unique in your workspace
milestonesarrayYes1 to 20 milestones, each { title, amount }: a title of 2 to 180 characters and a whole rupiah amount from 10,000 to 50,000,000,000
fee_bearerstringNobuyer, seller or split. Default: your default_fee_bearer
fee_split_buyer_pctintegerNo0 to 100, the buyer's share with split only. Default: your default_split_buyer_pct
metadataobjectNoUp to 20 string values, keys of 1 to 40 letters, digits, ., - or _, values up to 500 characters

RekberPay checks, in one step: both refs are active links of yours, the buyer and the seller are different people and in good standing, both are within the non-KYC limits (one budget across RekberPay and every partner), and the deal fits your daily cap.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 5f607182-93a4-4fb0-a1c2-3d4e5f607182" \
  -H "Content-Type: application/json" \
  -d '{
    "buyer_ref": "buyer-1001",
    "seller_ref": "seller-2001",
    "title": "Custom teak desk",
    "partner_order_ref": "ORD-55020",
    "milestones": [
      {
        "title": "Deposit",
        "amount": 1000000
      },
      {
        "title": "Final payment",
        "amount": 500000
      }
    ],
    "fee_bearer": "split",
    "fee_split_buyer_pct": 50,
    "metadata": {
      "listing_id": "L-88123"
    }
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '5f607182-93a4-4fb0-a1c2-3d4e5f607182',
    },
    body: JSON.stringify({
        buyer_ref: "buyer-1001",
        seller_ref: "seller-2001",
        title: "Custom teak desk",
        partner_order_ref: "ORD-55020",
        milestones: [
            {
                title: "Deposit",
                amount: 1000000,
            },
            {
                title: "Final payment",
                amount: 500000,
            },
        ],
        fee_bearer: "split",
        fee_split_buyer_pct: 50,
        metadata: {
            listing_id: "L-88123",
        },
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "5f607182-93a4-4fb0-a1c2-3d4e5f607182",
    },
    json={
        "buyer_ref": "buyer-1001",
        "seller_ref": "seller-2001",
        "title": "Custom teak desk",
        "partner_order_ref": "ORD-55020",
        "milestones": [
            {
                "title": "Deposit",
                "amount": 1000000,
            },
            {
                "title": "Final payment",
                "amount": 500000,
            },
        ],
        "fee_bearer": "split",
        "fee_split_buyer_pct": 50,
        "metadata": {
            "listing_id": "L-88123",
        },
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 5f607182-93a4-4fb0-a1c2-3d4e5f607182',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'buyer_ref' => 'buyer-1001',
        'seller_ref' => 'seller-2001',
        'title' => 'Custom teak desk',
        'partner_order_ref' => 'ORD-55020',
        'milestones' => [
            [
                'title' => 'Deposit',
                'amount' => 1000000,
            ],
            [
                'title' => 'Final payment',
                'amount' => 500000,
            ],
        ],
        'fee_bearer' => 'split',
        'fee_split_buyer_pct' => 50,
        'metadata' => [
            'listing_id' => 'L-88123',
        ],
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
        "reference": "DEAL-2026-0042",
        "partner_order_ref": "ORD-55020",
        "title": "Custom teak desk",
        "status": "awaiting_funding",
        "is_test": true,
        "buyer_ref": "buyer-1001",
        "seller_ref": "seller-2001",
        "currency": "IDR",
        "total_amount": 1500000,
        "funded_amount": 0,
        "released_amount": 0,
        "fee_bearer": "split",
        "fee_split_buyer_pct": 50,
        "platform_fee": 30000,
        "buyer_fee": 15000,
        "seller_fee": 15000,
        "metadata": {
            "listing_id": "L-88123"
        },
        "milestones": [
            {
                "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
                "sequence": 1,
                "title": "Deposit",
                "status": "awaiting_payment",
                "amount": 1000000,
                "buyer_fee": 10000,
                "seller_fee": 10000,
                "funding_amount": 1010000,
                "payout_amount": 990000,
                "refund_amount": null,
                "paid_at": null,
                "courier": null,
                "tracking_number": null,
                "shipped_at": null,
                "received_at": null,
                "received_by": null,
                "auto_release_at": null,
                "release_at": null,
                "released_at": null,
                "extend_count": 0,
                "disputed_at": null
            },
            {
                "id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
                "sequence": 2,
                "title": "Final payment",
                "status": "awaiting_payment",
                "amount": 500000,
                "buyer_fee": 5000,
                "seller_fee": 5000,
                "funding_amount": 505000,
                "payout_amount": 495000,
                "refund_amount": null,
                "paid_at": null,
                "courier": null,
                "tracking_number": null,
                "shipped_at": null,
                "received_at": null,
                "received_by": null,
                "auto_release_at": null,
                "release_at": null,
                "released_at": null,
                "extend_count": 0,
                "disputed_at": null
            }
        ],
        "created_at": "2026-10-21T08:00:00.000Z",
        "updated_at": "2026-10-21T08:00:00.000Z"
    }
}

Deal status: awaiting_funding, funded (every open milestone paid), in_progress, disputed (any milestone disputed), completed (every milestone finished, at least one released) or cancelled. Milestone status: awaiting_payment, paid, shipped, received, release_pending, released, disputed, refunded or cancelled.

Milestone fieldMeaning
funding_amountWhat the buyer pays: amount plus buyer_fee (the payment method fee comes on top at checkout)
payout_amountWhat the seller receives on release: amount minus seller_fee, or the seller part of a split
refund_amountWhat a dispute sent back to the buyer, or null
received_bybuyer, partner, auto or admin once it is released or pending release
auto_release_at, release_atWhen the automatic release or your pending confirm fires

Errors ​

StatusCodeCause
400INVALID_BODYA field is missing or not valid
400INVALID_FEE_ALLOCATIONfee_split_buyer_pct without split, or a percent outside 0 to 100
404USER_NOT_FOUNDNo active user with this buyer_ref or seller_ref (see field)
409DUPLICATE_ORDER_REFA deal with this partner_order_ref already exists
409DAILY_CAP_REACHEDThe deal would pass your daily cap. Carries daily_cap_idr and used_today_idr
409DEAL_CREATE_BUSYAnother deal is being created at the same moment. Retry
422SAME_PARTYThe buyer and the seller are the same person
422USER_NOT_AVAILABLEThe buyer or seller cannot take part in a new order (see field)
422LIMIT_EXCEEDEDThe buyer or seller cannot take a deal this size without KYC (reason NONKYC_LIMIT_AMOUNT), or their KYC status blocks it. Carries party and reason. The 24 hour count and the lifetime total are checked at checkout, not here
422FEE_EXCEEDS_AMOUNTA milestone is too small for the seller's share of the fee
Plus the errors every Platform route can answer

Get a deal ​

GET/v1/enterprise/platform/deals/:id

One deal with all its milestones. Use it to check a status after a webhook.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7');
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

json
{
    "success": true,
    "data": {
        "id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
        "reference": "DEAL-2026-0042",
        "partner_order_ref": "ORD-55020",
        "title": "Custom teak desk",
        "status": "in_progress",
        "is_test": true,
        "buyer_ref": "buyer-1001",
        "seller_ref": "seller-2001",
        "currency": "IDR",
        "total_amount": 1500000,
        "funded_amount": 1000000,
        "released_amount": 0,
        "fee_bearer": "split",
        "fee_split_buyer_pct": 50,
        "platform_fee": 30000,
        "buyer_fee": 15000,
        "seller_fee": 15000,
        "metadata": {
            "listing_id": "L-88123"
        },
        "milestones": [
            {
                "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
                "sequence": 1,
                "title": "Deposit",
                "status": "shipped",
                "amount": 1000000,
                "buyer_fee": 10000,
                "seller_fee": 10000,
                "funding_amount": 1010000,
                "payout_amount": 990000,
                "refund_amount": null,
                "paid_at": "2026-10-21T08:12:40.000Z",
                "courier": "JNE",
                "tracking_number": "JNE0012345678",
                "shipped_at": "2026-10-21T09:00:00.000Z",
                "received_at": null,
                "received_by": null,
                "auto_release_at": "2026-10-24T09:00:00.000Z",
                "release_at": null,
                "released_at": null,
                "extend_count": 0,
                "disputed_at": null
            },
            {
                "id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
                "sequence": 2,
                "title": "Final payment",
                "status": "awaiting_payment",
                "amount": 500000,
                "buyer_fee": 5000,
                "seller_fee": 5000,
                "funding_amount": 505000,
                "payout_amount": 495000,
                "refund_amount": null,
                "paid_at": null,
                "courier": null,
                "tracking_number": null,
                "shipped_at": null,
                "received_at": null,
                "received_by": null,
                "auto_release_at": null,
                "release_at": null,
                "released_at": null,
                "extend_count": 0,
                "disputed_at": null
            }
        ],
        "created_at": "2026-10-21T08:00:00.000Z",
        "updated_at": "2026-10-21T09:00:00.000Z"
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo such deal in your workspace
Plus the errors every Platform route can answer

List deals ​

GET/v1/enterprise/platform/deals

Your marketplace deals, newest first. Page with before: pass pagination.next_before from the previous page.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Query parameters ​

NameTypeRequiredDescription
userstringNoOnly deals where this partner_user_ref is the buyer or the seller
statusstringNoA deal status: awaiting_funding, funded, in_progress, disputed, completed or cancelled
beforeUUIDNoThe id of the last deal of the previous page
limitintegerNo1 to 50, default 20

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/deals?user=seller-2001&status=awaiting_funding&limit=20" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals?user=seller-2001&status=awaiting_funding&limit=20', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/deals?user=seller-2001&status=awaiting_funding&limit=20",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals?user=seller-2001&status=awaiting_funding&limit=20');
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

json
{
    "success": true,
    "data": [
        {
            "id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
            "reference": "DEAL-2026-0042",
            "partner_order_ref": "ORD-55020",
            "title": "Custom teak desk",
            "status": "awaiting_funding",
            "is_test": true,
            "buyer_ref": "buyer-1001",
            "seller_ref": "seller-2001",
            "currency": "IDR",
            "total_amount": 1500000,
            "funded_amount": 0,
            "released_amount": 0,
            "fee_bearer": "split",
            "fee_split_buyer_pct": 50,
            "platform_fee": 30000,
            "buyer_fee": 15000,
            "seller_fee": 15000,
            "metadata": {
                "listing_id": "L-88123"
            },
            "milestones": [
                {
                    "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
                    "sequence": 1,
                    "title": "Deposit",
                    "status": "awaiting_payment",
                    "amount": 1000000,
                    "buyer_fee": 10000,
                    "seller_fee": 10000,
                    "funding_amount": 1010000,
                    "payout_amount": 990000,
                    "refund_amount": null,
                    "paid_at": null,
                    "courier": null,
                    "tracking_number": null,
                    "shipped_at": null,
                    "received_at": null,
                    "received_by": null,
                    "auto_release_at": null,
                    "release_at": null,
                    "released_at": null,
                    "extend_count": 0,
                    "disputed_at": null
                },
                {
                    "id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
                    "sequence": 2,
                    "title": "Final payment",
                    "status": "awaiting_payment",
                    "amount": 500000,
                    "buyer_fee": 5000,
                    "seller_fee": 5000,
                    "funding_amount": 505000,
                    "payout_amount": 495000,
                    "refund_amount": null,
                    "paid_at": null,
                    "courier": null,
                    "tracking_number": null,
                    "shipped_at": null,
                    "received_at": null,
                    "received_by": null,
                    "auto_release_at": null,
                    "release_at": null,
                    "released_at": null,
                    "extend_count": 0,
                    "disputed_at": null
                }
            ],
            "created_at": "2026-10-21T08:00:00.000Z",
            "updated_at": "2026-10-21T08:00:00.000Z"
        }
    ],
    "pagination": {
        "limit": 20,
        "has_more": false,
        "next_before": null
    }
}

Errors ​

StatusCodeCause
400INVALID_QUERYA query value is not valid
Plus the errors every Platform route can answer

Cancel a deal ​

POST/v1/enterprise/platform/deals/:id/cancel

Cancels a deal before any milestone is paid. Every unpaid milestone becomes cancelled. After a payment, open a dispute instead.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/cancel" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 60718293-a4b5-40c1-b2d3-4e5f60718293"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/cancel', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': '60718293-a4b5-40c1-b2d3-4e5f60718293',
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/cancel",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "60718293-a4b5-40c1-b2d3-4e5f60718293",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/cancel');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: 60718293-a4b5-40c1-b2d3-4e5f60718293',
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
        "reference": "DEAL-2026-0042",
        "partner_order_ref": "ORD-55020",
        "title": "Custom teak desk",
        "status": "cancelled",
        "is_test": true,
        "buyer_ref": "buyer-1001",
        "seller_ref": "seller-2001",
        "currency": "IDR",
        "total_amount": 1500000,
        "funded_amount": 0,
        "released_amount": 0,
        "fee_bearer": "split",
        "fee_split_buyer_pct": 50,
        "platform_fee": 0,
        "buyer_fee": 0,
        "seller_fee": 0,
        "metadata": {
            "listing_id": "L-88123"
        },
        "milestones": [
            {
                "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
                "sequence": 1,
                "title": "Deposit",
                "status": "cancelled",
                "amount": 1000000,
                "buyer_fee": 10000,
                "seller_fee": 10000,
                "funding_amount": 1010000,
                "payout_amount": 990000,
                "refund_amount": null,
                "paid_at": null,
                "courier": null,
                "tracking_number": null,
                "shipped_at": null,
                "received_at": null,
                "received_by": null,
                "auto_release_at": null,
                "release_at": null,
                "released_at": null,
                "extend_count": 0,
                "disputed_at": null
            },
            {
                "id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
                "sequence": 2,
                "title": "Final payment",
                "status": "cancelled",
                "amount": 500000,
                "buyer_fee": 5000,
                "seller_fee": 5000,
                "funding_amount": 505000,
                "payout_amount": 495000,
                "refund_amount": null,
                "paid_at": null,
                "courier": null,
                "tracking_number": null,
                "shipped_at": null,
                "received_at": null,
                "received_by": null,
                "auto_release_at": null,
                "release_at": null,
                "released_at": null,
                "extend_count": 0,
                "disputed_at": null
            }
        ],
        "created_at": "2026-10-21T08:00:00.000Z",
        "updated_at": "2026-10-21T08:20:00.000Z"
    }
}

The deal's platform_fee, buyer_fee and seller_fee drop to 0, since no milestone is left to charge; each milestone keeps its own fee shares and total_amount stays the sum of every milestone. You get deal.cancelled.

Errors ​

StatusCodeCause
404NOT_FOUNDNo such deal in your workspace
409INVALID_STATEA milestone is already paid, or the deal is already cancelled
409PAYMENT_IN_PROGRESSA checkout payment can still land. Wait until it is confirmed or expires
Plus the errors every Platform route can answer
POST/v1/enterprise/platform/deals/:id/milestones/:mid/checkout

A RekberPay checkout URL for one milestone, valid for 30 minutes. Send the buyer there; no sign-in is needed. See Checkout for buyers.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/checkout" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 718293a4-b5c6-41d2-83e4-5f60718293a4"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/checkout', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': '718293a4-b5c6-41d2-83e4-5f60718293a4',
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/checkout",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "718293a4-b5c6-41d2-83e4-5f60718293a4",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/checkout');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: 718293a4-b5c6-41d2-83e4-5f60718293a4',
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "url": "https://rekberpay.com/checkout/p/rpt.eyJwIjoiY2hlY2tvdXQiLCJ4IjoxfQ.q3J8vYc0bX2mN5tK9wE1rL7sA4dF6hG8jZ0pQ2uV4yI",
        "expires_at": "2026-10-21T08:30:00.000Z"
    }
}

The link starts one payment. The buyer page answers 409 CHECKOUT_USED for a second payment with the same link, 409 CHECKOUT_CHANGED when the order changed after you made it, and 410 LINK_EXPIRED once it is dead. After a gateway payment the buyer lands on https://rekberpay.com/checkout/p/r/<payment_link_id>, a read-only status page kept for 30 days.

Errors ​

StatusCodeCause
404NOT_FOUNDNo such deal or milestone in your workspace
409INVALID_STATEThe milestone is not waiting for payment
409USER_NOT_AVAILABLEThe buyer is no longer linked to you
422LIMIT_EXCEEDEDThe buyer or the seller has no room left in the non-KYC limits for this milestone. Carries party and reason (NONKYC_LIMIT_AMOUNT, NONKYC_LIMIT_DAILY or NONKYC_LIMIT_LIFETIME)
Plus the errors every Platform route can answer

A milestone counts toward the buyer's and the seller's non-KYC limits from checkout or payment, never when the deal is created, so an order nobody pays holds none of their limits. The buyer page checks the limits again when the buyer pays, and refuses a second payment while an earlier one on the milestone can still be paid (409 PAYMENT_IN_PROGRESS).

Simulate a payment (sandbox) ​

A sandbox order is paid with test money. The checkout page has a Simulasikan pembayaran button, or your server calls the buyer checkout API with the token (the part of the URL after /checkout/p/). No key is sent: the token is the only credential, and the call spends it like a real payment. It is limited to 20 calls a minute per IP address.

bash
CHECKOUT_TOKEN=${CHECKOUT_URL##*/checkout/p/}
curl -X POST "https://api.rekberpay.com/v1/platform/checkout/$CHECKOUT_TOKEN/simulate"
json
{
    "success": true,
    "data": {
        "milestone_status": "paid",
        "is_test": true
    }
}

You get milestone.paid. Its own answers:

StatusCodeCause
404NOT_FOUNDA live order: only sandbox orders can be simulated
409CHECKOUT_USEDThis token already started a payment
409CHECKOUT_CHANGEDThe order changed after the link was made
409INVALID_STATEThe milestone is not waiting for payment
410LINK_EXPIREDThe token is invalid or expired
423PLATFORM_FROZENRekberPay paused your Platform access
429RATE_LIMITEDMore than 20 calls a minute from this IP

Cancel a milestone ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/cancel

Cancels one milestone that is still awaiting_payment, also when other milestones of the deal are paid. Use it when part of an order is called off. The deal status is derived again: it is completed once every other milestone is released or refunded (with at least one released), and cancelled when nothing else is left, which also sends deal.cancelled. There is no milestone event for this; read the answer, which is the deal.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d/cancel" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 8293a4b5-c6d7-42e3-94f5-60718293a4b5"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d/cancel', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': '8293a4b5-c6d7-42e3-94f5-60718293a4b5',
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d/cancel",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "8293a4b5-c6d7-42e3-94f5-60718293a4b5",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d/cancel');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: 8293a4b5-c6d7-42e3-94f5-60718293a4b5',
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK. The deal as Get a deal shows it. Here the first milestone was released, so cancelling the second completes the deal:

json
{
    "success": true,
    "data": {
        "id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
        "reference": "DEAL-2026-0042",
        "partner_order_ref": "ORD-55020",
        "status": "completed",
        "total_amount": 1500000,
        "funded_amount": 1000000,
        "released_amount": 1000000,
        "milestones": [
            { "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8", "sequence": 1, "status": "released", "amount": 1000000 },
            { "id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d", "sequence": 2, "status": "cancelled", "amount": 500000 }
        ]
    }
}

Every other deal field is there too; the milestones are shortened here. total_amount stays the sum of every milestone, cancelled ones included.

Errors ​

StatusCodeCause
400INVALID_BODYThe body is not empty
404NOT_FOUNDNo such deal or milestone in your workspace
409INVALID_STATEThe milestone is already paid or already cancelled. After a payment, open a dispute instead
409PAYMENT_IN_PROGRESSA checkout payment on this milestone can still land. Wait until it is confirmed or expires
Plus the errors every Platform route can answer

Ship a milestone ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/ship

Reports the shipment for the seller: paid to shipped. Starts the automatic release clock (72 hours with a tracking number, 36 without, by default). The answer is the milestone.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Body (JSON) ​

NameTypeRequiredDescription
courierstringYes1 to 60 characters
tracking_numberstring | nullNo1 to 80 characters

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/ship" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 8293a4b5-c6d7-42e3-94f5-60718293a4b5" \
  -H "Content-Type: application/json" \
  -d '{
    "courier": "JNE",
    "tracking_number": "JNE0012345678"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/ship', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '8293a4b5-c6d7-42e3-94f5-60718293a4b5',
    },
    body: JSON.stringify({
        courier: "JNE",
        tracking_number: "JNE0012345678",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/ship",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "8293a4b5-c6d7-42e3-94f5-60718293a4b5",
    },
    json={
        "courier": "JNE",
        "tracking_number": "JNE0012345678",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/ship');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 8293a4b5-c6d7-42e3-94f5-60718293a4b5',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'courier' => 'JNE',
        'tracking_number' => 'JNE0012345678',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
        "sequence": 1,
        "title": "Deposit",
        "status": "shipped",
        "amount": 1000000,
        "buyer_fee": 10000,
        "seller_fee": 10000,
        "funding_amount": 1010000,
        "payout_amount": 990000,
        "refund_amount": null,
        "paid_at": "2026-10-21T08:12:40.000Z",
        "courier": "JNE",
        "tracking_number": "JNE0012345678",
        "shipped_at": "2026-10-21T09:00:00.000Z",
        "received_at": null,
        "received_by": null,
        "auto_release_at": "2026-10-24T09:00:00.000Z",
        "release_at": null,
        "released_at": null,
        "extend_count": 0,
        "disputed_at": null
    }
}

You get milestone.shipped.

Errors ​

StatusCodeCause
400INVALID_BODYcourier or tracking_number is missing or too long
404NOT_FOUNDNo such deal or milestone in your workspace
409INVALID_STATEOnly a paid milestone can be shipped
Plus the errors every Platform route can answer

Confirm receipt ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/confirm

Confirms receipt for the buyer: shipped to release_pending. The release waits confirm_delay_hours (6 by default). The buyer is told by email and in the app and can stop it with Bukan saya, ajukan sengketa. See Shipping, confirm and auto release.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders:confirm, and write on the key. Off by default; RekberPay must allow it in your terms.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Send an empty JSON object, or no body. In the sandbox the answer adds release_notice_url, the page the buyer would get.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/confirm" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 93a4b5c6-d7e8-43f4-a506-718293a4b5c6" \
  -H "Content-Type: application/json" \
  -d '{}'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/confirm', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '93a4b5c6-d7e8-43f4-a506-718293a4b5c6',
    },
    body: JSON.stringify({}),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/confirm",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "93a4b5c6-d7e8-43f4-a506-718293a4b5c6",
    },
    json={},
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/confirm');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 93a4b5c6-d7e8-43f4-a506-718293a4b5c6',
    ],
    CURLOPT_POSTFIELDS => json_encode(new stdClass()),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
        "sequence": 1,
        "title": "Deposit",
        "status": "release_pending",
        "amount": 1000000,
        "buyer_fee": 10000,
        "seller_fee": 10000,
        "funding_amount": 1010000,
        "payout_amount": 990000,
        "refund_amount": null,
        "paid_at": "2026-10-21T08:12:40.000Z",
        "courier": "JNE",
        "tracking_number": "JNE0012345678",
        "shipped_at": "2026-10-21T09:00:00.000Z",
        "received_at": "2026-10-22T04:00:00.000Z",
        "received_by": "partner",
        "auto_release_at": null,
        "release_at": "2026-10-22T10:00:00.000Z",
        "released_at": null,
        "extend_count": 0,
        "disputed_at": null,
        "release_notice_url": "https://rekberpay.com/release/rpt.eyJwIjoicmVsZWFzZV9ub3RpY2UiLCJ4IjoxfQ.Zr8nP2kW5vX1cM7qT4yB9sL3hF6dJ0aG2eU5iO8wN3m"
    }
}

You get milestone.release_pending now and milestone.released at release_at, unless the buyer disputes first.

Errors ​

StatusCodeCause
400INVALID_BODYThe body is not empty
404NOT_FOUNDNo such deal or milestone in your workspace
409INVALID_STATEOnly a shipped milestone can be confirmed
409USER_NOT_AVAILABLEThe buyer is no longer linked to you
Plus the errors every Platform route can answer

Extend the auto release ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/extend

The buyer's one extension of the automatic release (48 hours by default), asked through you. The new clock counts from auto_release_at or from now, whichever is later.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Send an empty JSON object, or no body.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/extend" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: a4b5c6d7-e8f9-4405-b617-8293a4b5c6d7" \
  -H "Content-Type: application/json" \
  -d '{}'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/extend', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'a4b5c6d7-e8f9-4405-b617-8293a4b5c6d7',
    },
    body: JSON.stringify({}),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/extend",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "a4b5c6d7-e8f9-4405-b617-8293a4b5c6d7",
    },
    json={},
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/extend');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: a4b5c6d7-e8f9-4405-b617-8293a4b5c6d7',
    ],
    CURLOPT_POSTFIELDS => json_encode(new stdClass()),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
        "sequence": 1,
        "title": "Deposit",
        "status": "shipped",
        "amount": 1000000,
        "buyer_fee": 10000,
        "seller_fee": 10000,
        "funding_amount": 1010000,
        "payout_amount": 990000,
        "refund_amount": null,
        "paid_at": "2026-10-21T08:12:40.000Z",
        "courier": "JNE",
        "tracking_number": "JNE0012345678",
        "shipped_at": "2026-10-21T09:00:00.000Z",
        "received_at": null,
        "received_by": null,
        "auto_release_at": "2026-10-26T09:00:00.000Z",
        "release_at": null,
        "released_at": null,
        "extend_count": 1,
        "disputed_at": null
    }
}

You get milestone.extended.

Errors ​

StatusCodeCause
400INVALID_BODYThe body is not empty
404NOT_FOUNDNo such deal or milestone in your workspace
409NOT_EXTENDABLEOnly a shipped milestone can be extended
409NO_ACTIVE_TIMERThe milestone has no running automatic release
409EXTEND_LIMIT_REACHEDIt was already extended the most times allowed
409EXTEND_DISABLEDRekberPay has switched extensions off
Plus the errors every Platform route can answer

Open a dispute ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/dispute

Opens a dispute for the buyer or the seller on a paid, shipped or release_pending milestone. Every timer stops and a pending confirm is cancelled. RekberPay's mediation team decides. See Disputes on marketplace orders.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Body (JSON) ​

NameTypeRequiredDescription
reasonstringYes10 to 700 characters, without < or >
raised_bystringYesbuyer or seller

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: b5c6d7e8-f90a-4516-8728-93a4b5c6d7e8" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "The desk arrived with a cracked top. Photos are in the chat.",
    "raised_by": "buyer"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'b5c6d7e8-f90a-4516-8728-93a4b5c6d7e8',
    },
    body: JSON.stringify({
        reason: "The desk arrived with a cracked top. Photos are in the chat.",
        raised_by: "buyer",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "b5c6d7e8-f90a-4516-8728-93a4b5c6d7e8",
    },
    json={
        "reason": "The desk arrived with a cracked top. Photos are in the chat.",
        "raised_by": "buyer",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: b5c6d7e8-f90a-4516-8728-93a4b5c6d7e8',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'reason' => 'The desk arrived with a cracked top. Photos are in the chat.',
        'raised_by' => 'buyer',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
        "sequence": 1,
        "title": "Deposit",
        "status": "disputed",
        "amount": 1000000,
        "buyer_fee": 10000,
        "seller_fee": 10000,
        "funding_amount": 1010000,
        "payout_amount": 990000,
        "refund_amount": null,
        "paid_at": "2026-10-21T08:12:40.000Z",
        "courier": "JNE",
        "tracking_number": "JNE0012345678",
        "shipped_at": "2026-10-21T09:00:00.000Z",
        "received_at": null,
        "received_by": null,
        "auto_release_at": null,
        "release_at": null,
        "released_at": null,
        "extend_count": 0,
        "disputed_at": "2026-10-22T02:00:00.000Z"
    }
}

You get dispute.opened, and later dispute.resolved with outcome:

outcomeMilestoneMoney
releasereleasedThe seller's partner balance gets payout_amount
refundrefundedThe buyer's partner balance gets amount plus buyer_fee. The payment method fee is not refunded
splitreleasedThe buyer gets refund_amount plus the same share of buyer_fee, the seller the rest minus the same share of seller_fee, rounded down
resumeshippedNothing moves. The automatic release restarts with the time that was left. A dispute opened during your pending confirm gets at least the full shipping clock again

Errors ​

StatusCodeCause
400INVALID_BODYreason or raised_by is missing or not valid
404NOT_FOUNDNo such deal or milestone in your workspace
409INVALID_STATEThe milestone cannot be disputed in its current status
Plus the errors every Platform route can answer

Add dispute evidence ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/dispute/evidence

Adds a statement and files to an open dispute, as multipart/form-data. Only RekberPay's mediation team sees them.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required. The key is bound to the path, not to the form: a reused key replays the first answer and the new form is not processed. Use a new key for every upload.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Body (multipart/form-data) ​

NameTypeRequiredDescription
notetextYesOne part, 3 to 700 characters, without < or >
filesfileYes1 to 5 parts with this name. JPG, PNG, WebP or PDF, up to 10 MB each. At most 50 files per dispute

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute/evidence" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: c6d7e8f9-0a1b-4627-9839-a4b5c6d7e8f9" \
  -F "note=Photos of the cracked top and the courier receipt." \
  -F "[email protected]" \
  -F "[email protected]"
js
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.append("note", "Photos of the cracked top and the courier receipt.");
form.append("files", new Blob([await readFile("desk-top.jpg")], { type: "image/jpeg" }), "desk-top.jpg");
form.append("files", new Blob([await readFile("courier-receipt.pdf")], { type: "application/pdf" }), "courier-receipt.pdf");

const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute/evidence', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': 'c6d7e8f9-0a1b-4627-9839-a4b5c6d7e8f9',
    },
    body: form,
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute/evidence",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "c6d7e8f9-0a1b-4627-9839-a4b5c6d7e8f9",
    },
    data={
        "note": "Photos of the cracked top and the courier receipt.",
    },
    files=[
        ("files", ("desk-top.jpg", open("desk-top.jpg", "rb"), "image/jpeg")),
        ("files", ("courier-receipt.pdf", open("courier-receipt.pdf", "rb"), "application/pdf")),
    ],
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/dispute/evidence');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: c6d7e8f9-0a1b-4627-9839-a4b5c6d7e8f9',
    ],
    CURLOPT_POSTFIELDS => [
        'note' => 'Photos of the cracked top and the courier receipt.',
        'files' => new CURLFile('desk-top.jpg', 'image/jpeg'),
    ],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "milestone_id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
        "status": "disputed",
        "files_added": 2
    }
}

The PHP example sends one file: a PHP array cannot repeat the files name. Send one call per file, each with its own Idempotency-Key.

Errors ​

StatusCodeCause
400INVALID_BODYNot multipart, no file, a part with another name, or the note is not valid
404NOT_FOUNDNo such deal or milestone in your workspace
409NOT_DISPUTEDThe dispute is not open
409EVIDENCE_LIMIT_REACHEDThe dispute already holds 50 files
413FILE_TOO_LARGEA file is over 10 MB
400TYPE_NOT_ALLOWEDA file is not JPG, PNG, WebP or PDF
400TYPE_MISMATCHThe content of a file does not match its type or extension
400IMAGE_DECODE_FAILEDAn image is damaged or not a real image
400PDF_ENCRYPTEDA PDF is locked, damaged or holds content that cannot be removed. Also PDF_PASSWORD_PROTECTED, PDF_ACTIVE_CONTENT, PDF_EMBEDDED_FILE, PDF_DAMAGED, PDF_UNSUPPORTED_ENCODING, PDF_TOO_MANY_PAGES. Send a photo or a re-saved PDF
429QUOTA_EXCEEDEDThe daily upload limit (200 files) of the member who made the key is reached
500STORAGE_FAILEDA file could not be stored. Nothing was added. Retry with a new Idempotency-Key
503PDF_CHECK_UNAVAILABLERekberPay cannot check PDFs right now. Retry later or send photos
Plus the errors every Platform route can answer

Advance a milestone timer (sandbox) ​

POST/v1/enterprise/platform/deals/:id/milestones/:mid/simulate/advance-time

Releases a sandbox milestone now, as if its time ran out. A shipped milestone is released like the automatic release (received_by: "auto", event milestone.auto_released). A release_pending milestone, after your confirm, is released like the end of the confirm wait (received_by: "partner", event milestone.released). The seller's test partner balance gets payout_amount. The answer is the milestone.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: orders, and write on the key.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.
  • Sandbox only. A Live request gets 404 NOT_FOUND, whatever the scope.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id
midUUIDYesThe milestone id

Send an empty JSON object, or no body.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/simulate/advance-time" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: d7e8f90a-1b2c-4738-a94a-b5c6d7e8f90a" \
  -H "Content-Type: application/json" \
  -d '{}'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/simulate/advance-time', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'd7e8f90a-1b2c-4738-a94a-b5c6d7e8f90a',
    },
    body: JSON.stringify({}),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/simulate/advance-time",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "d7e8f90a-1b2c-4738-a94a-b5c6d7e8f90a",
    },
    json={},
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/milestones/4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8/simulate/advance-time');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: d7e8f90a-1b2c-4738-a94a-b5c6d7e8f90a',
    ],
    CURLOPT_POSTFIELDS => json_encode(new stdClass()),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "4f5a6b7c-8d9e-4fa0-b1c2-d3e4f5a6b7c8",
        "sequence": 1,
        "title": "Deposit",
        "status": "released",
        "amount": 1000000,
        "buyer_fee": 10000,
        "seller_fee": 10000,
        "funding_amount": 1010000,
        "payout_amount": 990000,
        "refund_amount": null,
        "paid_at": "2026-10-21T08:12:40.000Z",
        "courier": "JNE",
        "tracking_number": "JNE0012345678",
        "shipped_at": "2026-10-21T09:00:00.000Z",
        "received_at": "2026-10-21T09:30:00.000Z",
        "received_by": "auto",
        "auto_release_at": null,
        "release_at": null,
        "released_at": "2026-10-21T09:30:00.000Z",
        "extend_count": 0,
        "disputed_at": null
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYThe body is not empty
404NOT_FOUNDA Live request, or no such deal or milestone in your workspace
409INVALID_STATEThe milestone is not shipped or release_pending, it is disputed, or it was already settled
Plus the errors every Platform route can answer

Read a balance ​

GET/v1/enterprise/platform/users/:ref/balance

The user's partner balance: money from your orders only, in whole rupiah. main_balance is added only when the person allowed you to manage their whole balance.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: balance:read. The key must be made by a finance member or higher.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/balance" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/balance', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/balance",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/balance');
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

json
{
    "success": true,
    "data": {
        "partner_balance": 990000
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo active link with this ref, or the ref is not yours
Plus the errors every Platform route can answer

List bank accounts ​

GET/v1/enterprise/platform/users/:ref/bank-accounts

The payout bank accounts you added for this user. Numbers are masked.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: withdraw. The key must be made by a finance member or higher.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts');
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

json
{
    "success": true,
    "data": [
        {
            "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
            "bank_code": "BCA",
            "account_number_masked": "•••• 7890",
            "holder_name": "Budi Santoso",
            "usable_after": "2026-10-22T08:00:00.000Z",
            "usable": false,
            "is_test": true,
            "created_at": "2026-10-21T08:00:00.000Z"
        }
    ]
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo active link with this ref, or the ref is not yours
Plus the errors every Platform route can answer

Add a bank account ​

POST/v1/enterprise/platform/users/:ref/bank-accounts

Adds a payout bank account for the user. The holder name must match the user's name on RekberPay, and the account can receive withdrawals only after its cooling-off (24 hours by default). See Balances and withdrawals.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: withdraw, and write on the key. The key must be made by a finance member or higher.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Body (JSON) ​

NameTypeRequiredDescription
bank_codestringYesThe bank code, 2 to 40 letters, digits or _, for example BCA, with no run of 10 or more digits (that is an account number). Stored in upper case and never editable. It sets the withdrawal fee: use one of the accepted bank codes for BI-FAST
account_numberstringYes5 to 30 digits, without spaces
holder_namestringYes1 to 120 characters. Must match the KTP name once KYC is approved, else the account name

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: e8f90a1b-2c3d-4849-ba5b-c6d7e8f90a1b" \
  -H "Content-Type: application/json" \
  -d '{
    "bank_code": "BCA",
    "account_number": "1234567890",
    "holder_name": "Budi Santoso"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'e8f90a1b-2c3d-4849-ba5b-c6d7e8f90a1b',
    },
    body: JSON.stringify({
        bank_code: "BCA",
        account_number: "1234567890",
        holder_name: "Budi Santoso",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "e8f90a1b-2c3d-4849-ba5b-c6d7e8f90a1b",
    },
    json={
        "bank_code": "BCA",
        "account_number": "1234567890",
        "holder_name": "Budi Santoso",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: e8f90a1b-2c3d-4849-ba5b-c6d7e8f90a1b',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'bank_code' => 'BCA',
        'account_number' => '1234567890',
        'holder_name' => 'Budi Santoso',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
        "bank_code": "BCA",
        "account_number_masked": "•••• 7890",
        "holder_name": "Budi Santoso",
        "usable_after": "2026-10-22T08:00:00.000Z",
        "usable": false,
        "is_test": true,
        "created_at": "2026-10-21T08:00:00.000Z"
    }
}

You get bank_account.added in the event feed. A person who controls their own account also gets an email.

Errors ​

StatusCodeCause
400INVALID_BODYA field is missing or not valid. The answer never repeats the number
404NOT_FOUNDNo active link with this ref, or the ref is not yours
409BANK_ACCOUNT_EXISTSThis bank and number are already saved. Carries bank_account_id
409BANK_ACCOUNT_LIMITThe user already has 10 payout bank accounts with you
422NAME_MISMATCHThe holder name does not match. Nothing is stored
Plus the errors every Platform route can answer

Skip a bank account cooling-off (sandbox) ​

POST/v1/enterprise/platform/users/:ref/bank-accounts/:id/simulate/skip-cooling-off

Makes a sandbox payout bank account usable now, so a withdrawal no longer answers 409 BANK_ACCOUNT_COOLING_OFF. An account that is already usable comes back unchanged. The answer is the bank account.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: withdraw, and write on the key. The key must be made by a finance member or higher.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.
  • Sandbox only. A Live request gets 404 NOT_FOUND, whatever the scope.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref
idUUIDYesThe bank account id

Send an empty JSON object, or no body.

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/simulate/skip-cooling-off" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: f90a1b2c-3d4e-495a-8b6c-d7e8f90a1b2c" \
  -H "Content-Type: application/json" \
  -d '{}'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/simulate/skip-cooling-off', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'f90a1b2c-3d4e-495a-8b6c-d7e8f90a1b2c',
    },
    body: JSON.stringify({}),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/simulate/skip-cooling-off",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "f90a1b2c-3d4e-495a-8b6c-d7e8f90a1b2c",
    },
    json={},
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/bank-accounts/5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d/simulate/skip-cooling-off');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: f90a1b2c-3d4e-495a-8b6c-d7e8f90a1b2c',
    ],
    CURLOPT_POSTFIELDS => json_encode(new stdClass()),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK

json
{
    "success": true,
    "data": {
        "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
        "bank_code": "BCA",
        "account_number_masked": "•••• 7890",
        "holder_name": "Budi Santoso",
        "usable_after": "2026-10-21T08:10:00.000Z",
        "usable": true,
        "is_test": true,
        "created_at": "2026-10-21T08:00:00.000Z"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYThe body is not empty
404NOT_FOUNDA Live request, or no active link with this ref
404BANK_ACCOUNT_NOT_FOUNDThe account is not this user's with you, or it was removed
Plus the errors every Platform route can answer

Request a withdrawal ​

POST/v1/enterprise/platform/users/:ref/withdrawals

Pays out part or all of the balance to a saved bank account. The usual RekberPay limits, fee (Withdrawal fees) and fraud screening apply. A large live withdrawal starts pending_review and waits for a second RekberPay approver. A sandbox withdrawal completes at once.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: withdraw, and write on the key. The key must be made by a finance member or higher.
  • Rate limit: 30 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref

Body (JSON) ​

NameTypeRequiredDescription
amountintegerYesWhole rupiah, the fee included
bank_account_idUUIDYesA saved bank account of this user, past its cooling-off
fromstringNopartner (default) or main. main needs full_balance consent and never works in the sandbox

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 990000,
    "bank_account_id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        amount: 990000,
        bank_account_id: "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    json={
        "amount": 990000,
        "bank_account_id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 990000,
        'bank_account_id' => '5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e",
        "partner_user_ref": "seller-2001",
        "status": "completed",
        "from": "partner",
        "amount": 990000,
        "fee": 4000,
        "fee_rail": "bifast",
        "net_amount": 986000,
        "bank_account_id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
        "is_test": true,
        "created_at": "2026-10-22T09:00:00.000Z",
        "updated_at": "2026-10-22T09:00:00.000Z",
        "processed_at": "2026-10-22T09:00:00.000Z"
    }
}

status is pending, pending_review, processing, completed or rejected. You get withdrawal.requested, then withdrawal.settled or withdrawal.failed (the money is back in the balance).

Errors ​

StatusCodeCause
400INVALID_BODYA field is missing or not valid
403RISK_BLOCKEDRekberPay screening refused it. The user can contact RekberPay support
404NOT_FOUNDNo active link with this ref, or main without full_balance consent
404BANK_ACCOUNT_NOT_FOUNDNo such bank account for this user
409BANK_ACCOUNT_COOLING_OFFThe bank account is still in cooling-off. Carries usable_after
409ACCOUNT_NOT_ACTIVEThe account is closing
409TRY_AGAINAnother change to this user was saved at the same moment. Nothing was done. Retry the same request
422INVALID_AMOUNTThe amount is outside the limits. Carries limits (min_idr, max_idr, step_idr). The whole partner balance may always be withdrawn, even under the minimum or off the step, while it is more than the fee
422INSUFFICIENT_BALANCEThe balance is too low
422FEE_EXCEEDS_AMOUNTThe withdrawal fee is not lower than the amount
422NAME_MISMATCHThe bank account holder no longer matches the user's name
422SANDBOX_MAIN_BALANCEA sandbox withdrawal asked for main
500WITHDRAWAL_FAILEDThe withdrawal could not be saved. Nothing was charged. Retry
Plus the errors every Platform route can answer

Withdrawal fees ​

A withdrawal pays one fee, set by the bank rail RekberPay pays it on. The fee comes out of amount, the bank receives net_amount, and the answer names the rail in fee_rail.

fee_railWhenFee (default)
bifastThe bank account's bank_code is one of the accepted bank codesRp4.000
online_transferAny other bank_code (a typo, a bank RekberPay does not list), or a listed bank RekberPay has marked as not on BI-FASTRp8.000
nullThe bank_code names an e-wallet: GOPAY, OVO, DANA, SHOPEEPAY or LINKAJA. It pays RekberPay's e-wallet withdrawal feeSet by RekberPay

The user's RekberPay loyalty discount or withdrawal fee voucher can lower the fee; fee is what was charged. RekberPay may change the two rail fees and the banks marked as not on BI-FAST: GET /v1/enterprise/platform-status returns the current ones in terms. A sandbox withdrawal is priced the same way, with test money.

A saved bank account cannot be edited. To move an account to BI-FAST, add it again with an accepted code; the new account waits out the cooling-off (24 hours by default) like any other.

Accepted bank codes ​

A bank_code gets BI-FAST when it is one of the codes below, or the bank's Bank Indonesia 3-digit code (sandi bank) written exactly, leading zeros included (014 is BCA, 14 is not recognised). Case, _ and a leading BANK do not matter, and common short names work too (CIMB for CIMB_NIAGA, JENIUS for BTPN). Anything else pays Online Transfer.

bank_codeBankBank Indonesia codeRail
BCABCA014BI-FAST
BRIBRI002BI-FAST
BNIBNI009BI-FAST
MANDIRIMandiri008BI-FAST
BSIBSI (Bank Syariah Indonesia)451BI-FAST
BTNBTN200BI-FAST
CIMB_NIAGACIMB Niaga022BI-FAST
PERMATAPermata013BI-FAST
DANAMONDanamon011BI-FAST
ALLO_BANKAllo Bank567BI-FAST
ANZANZ Indonesia061BI-FAST
BANK_ACEH_SYARIAHBank Aceh Syariah116BI-FAST
ALADIN_SYARIAHBank Aladin Syariah947BI-FAST
AMARBank Amar Indonesia531BI-FAST
ARTHA_GRAHABank Artha Graha037BI-FAST
BANK_BANTENBank Banten137BI-FAST
BANK_BENGKULUBank Bengkulu133BI-FAST
BJBBank BJB (Jawa Barat dan Banten)110BI-FAST
BJB_SYARIAHBank BJB Syariah425BI-FAST
BPD_DIYBank BPD DIY112BI-FAST
BUMI_ARTABank Bumi Arta076BI-FAST
BANK_CAPITALBank Capital Indonesia054BI-FAST
COMMONWEALTHBank CommonwealthnoneBI-FAST
BANK_DKIBank DKI111BI-FAST
GANESHABank Ganesha161BI-FAST
BANK_INABank Ina Perdana513BI-FAST
BANK_INDEXBank Index Selindo555BI-FAST
BANK_JAMBIBank Jambi115BI-FAST
JASA_JAKARTABank Jasa Jakarta472BI-FAST
BANK_JATENGBank Jateng113BI-FAST
BANK_JATIMBank Jatim114BI-FAST
BANK_KALBARBank Kalbar123BI-FAST
BANK_KALSELBank Kalsel122BI-FAST
BANK_KALTENGBank Kalteng125BI-FAST
BANK_KALTIMTARABank Kaltimtara124BI-FAST
BANK_LAMPUNGBank Lampung121BI-FAST
BANK_MALUKU_MALUTBank Maluku Malut131BI-FAST
MANDIRI_TASPENBank Mandiri Taspen564BI-FAST
MASPIONBank Maspion157BI-FAST
MAYAPADABank Mayapada097BI-FAST
MAYORABank MayoranoneBI-FAST
MEGA_SYARIAHBank Mega Syariah506BI-FAST
MESTIKABank Mestika Dharma151BI-FAST
MUAMALATBank Muamalat147BI-FAST
MULTIARTABank Multiarta Sentosa548BI-FAST
BANK_NAGARIBank Nagari118BI-FAST
NEO_COMMERCEBank Neo Commerce490BI-FAST
NOBUBank Nobu503BI-FAST
BANK_NTB_SYARIAHBank NTB Syariah128BI-FAST
BANK_NTTBank NTT130BI-FAST
BANK_OF_AMERICABank of America033BI-FAST
BANK_OF_CHINABank of China069BI-FAST
BANK_OKEBank Oke Indonesia526BI-FAST
BANK_PAPUABank Papua132BI-FAST
BANK_RAYABank Raya494BI-FAST
RESONA_PERDANIABank Resona Perdania047BI-FAST
BANK_RIAU_KEPRIBank Riau Kepri Syariah119BI-FAST
SAMPOERNABank Sahabat Sampoerna523BI-FAST
SINARMASBank Sinarmas153BI-FAST
BANK_SULSELBARBank Sulselbar126BI-FAST
BANK_SULTENGBank Sulteng134BI-FAST
BANK_SULTRABank Sultra135BI-FAST
BANK_SULUTGOBank SulutGo127BI-FAST
BANK_SUMSEL_BABELBank Sumsel Babel120BI-FAST
BANK_SUMUTBank Sumut117BI-FAST
VICTORIABank Victoria566BI-FAST
WOORI_SAUDARABank Woori Saudara212BI-FAST
BCA_SYARIAHBCA Syariah536BI-FAST
BLUBlu by BCA Digital501BI-FAST
BNP_PARIBASBNP Paribas Indonesia057BI-FAST
BPD_BALIBPD Bali129BI-FAST
BTPNBTPN (Jenius)213BI-FAST
BTPN_SYARIAHBTPN Syariah547BI-FAST
CCBChina Construction Bank Indonesia036BI-FAST
CITIBANKCitibank031BI-FAST
CTBCCTBC Indonesia949BI-FAST
DBSDBS Indonesia046BI-FAST
HSBCHSBC Indonesia087BI-FAST
IBKIBK Indonesia945BI-FAST
ICBCICBC Indonesia164BI-FAST
JTRUSTJ Trust Bank Indonesia095BI-FAST
JAGOJago542BI-FAST
JPMORGANJPMorgan Chase Bank032BI-FAST
KB_BUKOPINKB Bukopin441BI-FAST
KB_BUKOPIN_SYARIAHKB Bukopin Syariah521BI-FAST
KEB_HANAKEB Hana Indonesia484BI-FAST
MAYBANKMaybank016BI-FAST
MEGAMega426BI-FAST
MIZUHOMizuho Indonesia048BI-FAST
MNCMNC Bank485BI-FAST
MUFGMUFG Bank042BI-FAST
OCBC_NISPOCBC NISP028BI-FAST
PANINPanin019BI-FAST
PANIN_DUBAI_SYARIAHPanin Dubai Syariah517BI-FAST
QNBQNB Indonesia167BI-FAST
SEABANKSeabank535BI-FAST
SHINHANShinhan Indonesia152BI-FAST
STANDARD_CHARTEREDStandard Chartered050BI-FAST
UOBUOB Indonesia023BI-FAST

Get a withdrawal ​

GET/v1/enterprise/platform/users/:ref/withdrawals/:id

One withdrawal you requested for this user.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: withdraw. The key must be made by a finance member or higher.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref
idUUIDYesThe withdrawal id

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals/6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals/6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals/6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/withdrawals/6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e');
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

json
{
    "success": true,
    "data": {
        "id": "6b7c8d9e-0f1a-4b2c-9d3e-4f5a6b7c8d9e",
        "partner_user_ref": "seller-2001",
        "status": "completed",
        "from": "partner",
        "amount": 990000,
        "fee": 4000,
        "fee_rail": "bifast",
        "net_amount": 986000,
        "bank_account_id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
        "is_test": true,
        "created_at": "2026-10-22T09:00:00.000Z",
        "updated_at": "2026-10-22T09:00:00.000Z",
        "processed_at": "2026-10-22T09:00:00.000Z"
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo such withdrawal for this user
Plus the errors every Platform route can answer

Rate a completed deal ​

POST/v1/enterprise/platform/deals/:id/ratings

Rates the other side of a completed deal for one side: rater: "buyer" rates the seller, rater: "seller" rates the buyer. You name a side, never a user. One rating per side per deal, whoever sends it first: you, or the person on RekberPay. The rating stays private until the other side rates (then both publish together) or for 3 days, whichever comes first. Published ratings count on the person's public RekberPay profile with a "via" badge naming your marketplace. Sandbox ratings never reach a public profile. See Ratings.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: ratings, and write on the key. New keys get ratings by default; a key made before ratings existed does not have it, so make a new key.
  • Rate limit: 60 a minute per key, plus the shared 300 a minute of the member who made the key.
  • Idempotency: Required with a workspace key: send Idempotency-Key (UUID v4), or the call answers 400 IDEMPOTENCY_KEY_REQUIRED. A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id

Body (JSON) ​

NameTypeRequiredDescription
raterstringYesbuyer or seller: the side that gives the rating
starsintegerYes1 to 5
reviewstringNoUp to 1000 characters, without <, > or "

Example request ​

bash
curl -X POST "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: c6d7e8f9-0a1b-4c2d-8e3f-a4b5c6d7e8f9" \
  -H "Content-Type: application/json" \
  -d '{
    "rater": "buyer",
    "stars": 5,
    "review": "Fast shipping, exactly as described."
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'c6d7e8f9-0a1b-4c2d-8e3f-a4b5c6d7e8f9',
    },
    body: JSON.stringify({
        rater: 'buyer',
        stars: 5,
        review: 'Fast shipping, exactly as described.',
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "c6d7e8f9-0a1b-4c2d-8e3f-a4b5c6d7e8f9",
    },
    json={
        "rater": "buyer",
        "stars": 5,
        "review": "Fast shipping, exactly as described.",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: c6d7e8f9-0a1b-4c2d-8e3f-a4b5c6d7e8f9',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'rater' => 'buyer',
        'stars' => 5,
        'review' => 'Fast shipping, exactly as described.',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

201 Created

json
{
    "success": true,
    "data": {
        "id": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
        "deal_id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
        "rater_role": "buyer",
        "submitted_by": "partner",
        "stars": 5,
        "review": "Fast shipping, exactly as described.",
        "created_at": "2026-10-25T03:00:00.000Z",
        "published_at": null
    }
}

published_at is set when the other side had already rated, so both ratings published now. You get rating.created (no stars or review), and rating.published for each rating once it publishes.

Errors ​

StatusCodeCause
400INVALID_BODYrater or stars is missing, or the review is too long or holds <, > or "
404NOT_FOUNDNo such deal in your workspace, or the rating side's person is no longer linked to you
409NOT_RATEABLEThe deal is not completed: a part still holds money or is disputed, or every paid part was refunded
409ALREADY_RATEDThis side already rated this deal, through you or on RekberPay
422USER_NOT_AVAILABLEThe rating side's account is suspended or closed
422SAME_PARTYThe buyer and the seller are the same person
Plus the errors every Platform route can answer

Get a deal's ratings ​

GET/v1/enterprise/platform/deals/:id/ratings

Who has rated a deal, and each rating once it is published. Before publication you only see submitted: true, never the stars or the review. A rating RekberPay hid stays submitted: true with rating: null.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: ratings.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
idUUIDYesThe deal id

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/deals/3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7/ratings');
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

json
{
    "success": true,
    "data": {
        "deal_id": "3e4f5a6b-7c8d-4e9f-a0b1-c2d3e4f5a6b7",
        "rateable": true,
        "buyer": { "submitted": true, "rating": null },
        "seller": { "submitted": false, "rating": null }
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo such deal in your workspace
Plus the errors every Platform route can answer

Get a user's reputation ​

GET/v1/enterprise/platform/users/:ref/reputation

A linked user's reputation across RekberPay: the same average, count and completed deals as their public profile, with the 10 latest public reviews (each with via_partner, the marketplace it came from, or null for a direct rekber). profile_url is null while the user has no public profile, for example an account you created that the owner has not claimed (see Unclaimed accounts). In a sandbox workspace only your own test ratings and test deals count, and profile_url is always null.

  • Auth: A workspace API key (Authorization: Bearer rpe_...) sent from your server, or a dashboard session. Personal API keys and app sessions get 403 KEY_NOT_ALLOWED.
  • Scope: users.
  • Rate limit: 120 a minute per key, plus the shared 300 a minute of the member who made the key.

Path parameters ​

NameTypeRequiredDescription
refstringYesYour partner_user_ref of an active link

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/reputation" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/reputation', {
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.get(
    "https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/reputation",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/users/seller-2001/reputation');
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

json
{
    "success": true,
    "data": {
        "partner_user_ref": "seller-2001",
        "average_rating": 4.5,
        "rating_count": 2,
        "completed_deals": 3,
        "is_verified": true,
        "profile_url": "https://rekberpay.com/u/toko_sari",
        "recent_ratings": [
            {
                "stars": 5,
                "review": "Fast shipping, exactly as described.",
                "created_at": "2026-10-25T03:00:00.000Z",
                "ratee_role": "seller",
                "via_partner": "Salken"
            }
        ]
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo active link with this ref
Plus the errors every Platform route can answer

Show unclaimed profiles (dashboard) ​

PUT/v1/enterprise/platform/settings/profile-visibility

Turns show_unclaimed_profiles on or off. While it is off (the default), an account you created stays off rekberpay.com/u until its owner claims it. Turning it on needs the consent statement ticked in the dashboard (consent: true and the current consent_version), and shows those accounts at once. Turning it off needs nothing and hides them again at once. Claimed accounts always follow the normal public rules, and sandbox accounts never show. Every change is recorded with who made it, and RekberPay may change it too. In the dashboard this is Settings, Public profiles, Show public profiles for accounts not claimed yet.

  • Auth: A dashboard session of a workspace member with the admin role or higher. Workspace API keys get 403 SESSION_REQUIRED: a person must tick the statement.
  • Scope: None. The member's role must allow admin.
  • Rate limit: 60 a minute per member.
  • Idempotency: Optional from the dashboard. A retry with the same Idempotency-Key and body returns the first answer.

Body (JSON) ​

NameTypeRequiredDescription
show_unclaimed_profilesbooleanYestrue to show unclaimed accounts you created, false to hide them
consentbooleanTo turn ontrue: the consent statement was ticked
consent_versionstringTo turn onThe statement version shown, today 2026-10-20

Example request ​

bash
curl -X PUT "https://api.rekberpay.com/v1/enterprise/platform/settings/profile-visibility" \
  -H "Cookie: $REKBERPAY_DASHBOARD_SESSION" \
  -H "X-CSRF-Token: $REKBERPAY_CSRF_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "show_unclaimed_profiles": true,
    "consent": true,
    "consent_version": "2026-10-20"
  }'
js
// From the dashboard in the browser: the session cookie goes along.
const res = await fetch('https://api.rekberpay.com/v1/enterprise/platform/settings/profile-visibility', {
    method: 'PUT',
    credentials: 'include',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken,
    },
    body: JSON.stringify({
        show_unclaimed_profiles: true,
        consent: true,
        consent_version: '2026-10-20',
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.put(
    "https://api.rekberpay.com/v1/enterprise/platform/settings/profile-visibility",
    headers={
        "Cookie": os.environ["REKBERPAY_DASHBOARD_SESSION"],
        "X-CSRF-Token": os.environ["REKBERPAY_CSRF_TOKEN"],
    },
    json={
        "show_unclaimed_profiles": True,
        "consent": True,
        "consent_version": "2026-10-20",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/platform/settings/profile-visibility');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Cookie: ' . getenv('REKBERPAY_DASHBOARD_SESSION'),
        'X-CSRF-Token: ' . getenv('REKBERPAY_CSRF_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'show_unclaimed_profiles' => true,
        'consent' => true,
        'consent_version' => '2026-10-20',
    ]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

Example response ​

200 OK: your settings, as Get your Platform settings answers them.

json
{
    "success": true,
    "data": {
        "enabled": true,
        "frozen": false,
        "show_unclaimed_profiles": true,
        "show_unclaimed_consent_at": "2026-10-25T03:00:00.000Z",
        "show_unclaimed_consent_version": "2026-10-20",
        "show_unclaimed_changed_by": "Rina Wulandari"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYshow_unclaimed_profiles is missing or not a boolean
403SESSION_REQUIREDA workspace API key: change this in the dashboard
403FORBIDDENYour workspace role is below admin
422CONSENT_REQUIREDTurning on without consent: true and the current consent_version
Plus the errors every Platform route can answer

Webhook events ​

Platform mode adds these 19 new events to the enterprise webhooks. They come only from Platform activity, so only a workspace that uses Platform receives them. Payloads name users by partner_user_ref (buyer_ref, seller_ref and partner_user_refs on order events, rater_ref and ratee_ref on rating events), never by internal user id.

EventWhen
platform.user.linkedYou created an account, or a person accepted your link request
platform.user.consent_declinedA person declined your link request
platform.user.claimedA person claimed an account you created
platform.user.unlinkedA link was revoked: by you, by the person, or because the person became RekberPay staff
platform.user.kyc_updatedA linked person's KYC status changed
platform.user.balance_scope_changedThe person changed how much of their balance you may manage (balance_scope)
bank_account.addedA payout bank account was added for a person (only the last 4 digits are sent)
milestone.paidThe buyer paid a milestone
milestone.shippedA milestone was shipped
milestone.receivedThe buyer confirmed receipt on RekberPay
milestone.release_pendingYou confirmed receipt; the release waits confirm_delay_hours
milestone.auto_releasedThe automatic release paid the seller
milestone.extendedThe automatic release was extended
withdrawal.requestedA withdrawal was created
withdrawal.settledA withdrawal was paid out
withdrawal.failedA withdrawal was rejected; the money is back in the balance
rating.createdA side of a deal rated the other side (by you or on RekberPay). Refs only: no stars or review until it publishes
rating.publishedA rating became public, with stars, review and published_at. Sent again after RekberPay edits or shows a hidden rating, so upsert by rating_id
rating.hiddenRekberPay hid or removed a rating. No reason is sent

Marketplace deals also send deal.created, deal.cancelled, milestone.released (a release by the buyer, after your confirm, or by a dispute decision), dispute.opened, dispute.evidence_added and dispute.resolved (with outcome: release, refund, split or resume). Their marketplace payloads are in the list below.

What every payload has ​

These events use the same envelope, signature and delivery and retry rules as every other enterprise webhook. Ignore keys you do not know: RekberPay may add fields.

  • data.next holds the fields listed for the event, plus three keys on every event: account_id (your workspace id), key_id (the workspace API key that made the call, null when a person on RekberPay, RekberPay staff or the worker acted) and is_test (true for sandbox data).
  • data.previous is null, or holds only the one or two keys the event moved from, named under each event.
  • Money is whole rupiah.
  • People appear only as your own refs. There is never a RekberPay user id, an email, an ID number or a full bank account number in a payload.
  • entity_type and entity_id name what changed: platform_user (the id is the link id the API returns for the user), milestone, deal, withdrawal or rating. deal_id is set when the event belongs to a deal, and amount carries the milestone, deal or withdrawal amount where the event has one.
  • livemode follows the webhook envelope: drop events with livemode: false in production.

The deal parties on an order event are buyer_ref, seller_ref and partner_order_ref (your order id, or null when you sent none). deal_status is the deal's status after the change, as in Get a deal.

Users and bank accounts ​

platform.user.linked ​

entity_type platform_user. You created an account, or a person accepted your link request. data.previous holds link_status: "pending_consent" after an accepted request.

FieldTypeMeaning
partner_user_refstringYour ref for the person
link_statusstringAlways active
originstringcreated (you made the account) or linked (the person already had one)
balance_scopestringpartner_funds, or full_balance when the person allowed you to manage their whole balance
claimedbooleantrue for a person who confirmed the link, false for an account you made that is not claimed yet

A person declined your link request. data.previous holds link_status: "pending_consent".

FieldTypeMeaning
partner_user_refstringYour ref for the person
link_statusstringAlways revoked
revoked_bystringAlways user

platform.user.claimed ​

A person claimed an account you created. data.previous holds claimed: false.

FieldTypeMeaning
partner_user_refstringYour ref for the person
claimedbooleanAlways true

platform.user.unlinked ​

A link was revoked. data.previous holds link_status: "active".

FieldTypeMeaning
partner_user_refstringYour ref for the person
link_statusstringAlways revoked
revoked_bystringpartner (you unlinked), user (the person removed you on RekberPay) or system (RekberPay unlinked them, for example because the person became RekberPay staff)

platform.user.kyc_updated ​

A linked person's KYC status changed. data.previous holds the old kyc_status.

FieldTypeMeaning
partner_user_refstringYour ref for the person
kyc_statusstringThe new status, as kyc_status on Get a user
submitted_bystringpartner only when you sent the KYC (the status is then pending). Absent on a review result

platform.user.balance_scope_changed ​

The person changed how much of their balance you may manage. Today a person can only lower it, in Pengaturan > Partner Terhubung (Settings, Connected partners). data.previous holds balance_scope: "full_balance". After this, from: "main" withdrawals answer 404.

FieldTypeMeaning
partner_user_refstringYour ref for the person
balance_scopestringThe new scope, partner_funds or full_balance
changed_bystringAlways user

bank_account.added ​

A payout bank account was added for a person, by you or by the person on RekberPay. Only the last 4 digits are sent.

FieldTypeMeaning
partner_user_refstringYour ref for the person
bank_account_idstringThe bank account id, as in Add a bank account
bank_codestringThe bank code, for example BCA
account_last4stringThe last 4 digits of the account number
cooling_off_hoursintegerHours before the account can receive a withdrawal

Orders ​

deal.created ​

entity_type deal, amount the total. An order was created.

FieldTypeMeaning
referencestringRekberPay's deal reference
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
milestonesintegerNumber of milestones
total_amountintegerSum of the milestone amounts
platform_feeintegerThe whole fee
buyer_fee, seller_feeintegerEach side's share of the fee
fee_bearerstringbuyer, seller or split
statusstringThe deal status

deal.cancelled ​

entity_type deal. You cancelled an order before payment. data.previous holds the old status.

FieldTypeMeaning
statusstringThe deal status after the cancel
partner_order_ref, buyer_ref, seller_refstring or nullThe parties

milestone.paid ​

entity_type milestone, amount the milestone amount. The buyer paid. data.previous holds status: "awaiting_payment".

FieldTypeMeaning
statusstringAlways paid
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
amountintegerThe milestone amount
buyer_feeintegerThe buyer's fee share
funding_amountintegerWhat the buyer paid: amount plus buyer_fee plus payment_method_fee
payment_method_feeintegerThe payment method's own fee
funding_methodstringqris, va, ewallet, wire, manual, sandbox or cstore (paid at an Alfamart or Indomaret cashier). New values can be added: treat an unknown one as a valid funding method.
deal_statusstringThe deal status after the payment

milestone.shipped ​

The milestone was shipped, by you or by the seller on RekberPay. data.previous holds the old status.

FieldTypeMeaning
statusstringAlways shipped
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
courierstringThe courier
tracking_numberstring or nullThe tracking number, if one was sent
auto_release_atstring or nullWhen the automatic release runs (ISO 8601), null when there is no clock
deal_statusstringThe deal status

milestone.received ​

The buyer confirmed receipt on RekberPay. The release follows at once, as milestone.released. data.previous holds status: "shipped".

FieldTypeMeaning
statusstringAlways received
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
received_bystringAlways buyer

milestone.release_pending ​

You confirmed receipt for the buyer. The money waits for the buyer's dispute window. data.previous holds the old status.

FieldTypeMeaning
statusstringAlways release_pending
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
received_bystringAlways partner
release_atstringWhen the release runs (ISO 8601)
confirm_delay_hoursintegerThe wait, 6 hours by default
deal_statusstringThe deal status

milestone.released ​

The seller was paid, after the buyer confirmed, after your confirm, or after a dispute decision. data.previous holds the old status.

FieldTypeMeaning
statusstringAlways released
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
received_bystringbuyer, partner (your confirm), auto or admin (a dispute decision)
amountintegerThe milestone amount
buyer_fee, seller_feeintegerEach side's fee share
payout_amountintegerWhat the seller's partner balance received: amount minus seller_fee
fee_revenueintegerThe fee RekberPay kept, buyer_fee plus seller_fee
deal_statusstringThe deal status

milestone.auto_released ​

The automatic release paid the seller because nobody acted in time. It has the same fields as milestone.released, with received_by always auto: status, partner_order_ref, buyer_ref, seller_ref, received_by, amount, buyer_fee, seller_fee, payout_amount, fee_revenue and deal_status.

milestone.extended ​

The buyer extended the automatic release, through you or on RekberPay. No amount. data.previous holds the old auto_release_at and extend_count.

FieldTypeMeaning
statusstringAlways shipped
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
auto_release_atstringThe new release time (ISO 8601)
extend_countintegerHow many times it was extended so far
added_hoursintegerHours this extension added

Disputes ​

dispute.opened ​

entity_type milestone, amount the milestone amount. data.previous holds the old status, and auto_release_at and release_at as they were.

FieldTypeMeaning
statusstringAlways disputed
reasonstringThe reason that was sent
raised_bystringbuyer or seller
viastringapi (you), buyer (on RekberPay) or release_notice (the buyer's page after your confirm)
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
partner_user_refsstring[]The refs of the buyer and the seller
release_cancelledbooleantrue when the dispute stopped a pending confirm of yours
timer_remaining_secondsinteger or nullSeconds left on the clock when it paused, restored on a resume
deal_statusstringThe deal status

dispute.evidence_added ​

Evidence was added to an open dispute by you. RekberPay staff see the files; they are never in the payload.

FieldTypeMeaning
statusstringAlways disputed
notestringThe statement
document_idsstring[]Ids of the files that were stored
partner_order_ref, buyer_ref, seller_refstring or nullThe parties
partner_user_refsstring[]The refs of the buyer and the seller

dispute.resolved ​

RekberPay's mediation team decided. data.previous holds status: "disputed". The fields depend on outcome. A release also sends milestone.released.

FieldOutcomesMeaning
outcomeallrelease, refund, split or resume
statusallThe milestone status after: released, refunded, released or shipped
noteallThe mediation team's note
partner_order_ref, buyer_ref, seller_refallThe parties
partner_user_refsallThe refs of the buyer and the seller
deal_statusallThe deal status
payout_amountreleaseWhat the seller's partner balance received: amount minus seller_fee
refund_amountrefund, splitThe principal that goes back to the buyer. A full refund is the whole milestone amount, the same number as refund_amount on the milestone
buyer_fee_refundedrefund, splitThe buyer's fee share that goes back with it. The payment method fee is never refunded
buyer_creditrefund, splitWhat the buyer's partner balance received: refund_amount plus buyer_fee_refunded
seller_creditsplitWhat the seller's partner balance received
fee_revenuesplitThe fee RekberPay kept
auto_release_atresumeThe restarted release time (ISO 8601), null when there was no clock
timer_remaining_secondsresumeThe seconds that were left, null when there was no clock

A split, as it arrives:

json
{
    "event": "dispute.resolved",
    "entity_type": "milestone",
    "amount": 1000000,
    "livemode": false,
    "data": {
        "previous": { "status": "disputed" },
        "next": {
            "outcome": "split",
            "status": "released",
            "note": "Part of the item arrived damaged.",
            "partner_order_ref": "ORD-55012",
            "buyer_ref": "buyer-1001",
            "seller_ref": "seller-2001",
            "partner_user_refs": ["buyer-1001", "seller-2001"],
            "deal_status": "completed",
            "refund_amount": 400000,
            "buyer_fee_refunded": 4000,
            "buyer_credit": 404000,
            "seller_credit": 594000,
            "fee_revenue": 12000,
            "account_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
            "key_id": null,
            "is_test": true
        }
    }
}

Withdrawals ​

withdrawal.requested ​

entity_type withdrawal, amount the withdrawal amount. A withdrawal was created, by you, or by the person from their partner balance on RekberPay while linked to you.

FieldTypeMeaning
partner_user_refstring or nullYour ref for the person. null only when they are no longer linked to you when the result lands
statusstringpending, or pending_review when a second RekberPay approver must approve it first
fromstringpartner (the partner balance) or main (the person's own RekberPay balance)
amountintegerThe amount, fee included
feeintegerThe withdrawal fee charged, set by its bank rail (see Withdrawal fees)
net_amountintegerWhat the bank receives: amount minus fee
bank_account_idstring or nullThe payout bank account

withdrawal.settled ​

A withdrawal was paid out. The fields are those of withdrawal.requested (partner_user_ref, status, from, amount, fee, net_amount, bank_account_id), with status always completed. data.previous holds the old status when the change came from RekberPay's payout team.

withdrawal.failed ​

A withdrawal was rejected and the money is back in the balance it came from. It has the same fields (partner_user_ref, status, from, amount, fee, net_amount and bank_account_id), with status always rejected.

Ratings ​

rating.created ​

entity_type rating, deal_id set. A side of a deal rated the other side, through you or on RekberPay. It is private: no stars and no review.

FieldTypeMeaning
rating_idstringThe rating id. Store ratings by it
deal_idstringThe deal
partner_order_refstring or nullYour order id
rater_rolestringbuyer or seller: the side that rated
rater_ref, ratee_refstring or nullThe refs of who rated and who was rated. null when the person is not linked to you
submitted_bystringpartner (you) or user (on RekberPay)
publishedbooleanAlways false

rating.published ​

The rating became public. It is sent again after RekberPay corrects a rating or shows a hidden one again, so upsert by rating_id. It has every field of rating.created (rating_id, deal_id, partner_order_ref, rater_role, rater_ref, ratee_ref, submitted_by), with published always true, and:

FieldTypeMeaning
starsinteger1 to 5
reviewstring or nullThe review text
published_atstringWhen it became public (ISO 8601)

rating.hidden ​

RekberPay hid or removed a rating. The reason is never sent.

FieldTypeMeaning
rating_idstringThe rating id
deal_idstringThe deal
partner_order_refstring or nullYour order id
ratee_refstring or nullThe ref of the person who was rated
hidden_atstringWhen it was hidden (ISO 8601)

Error codes ​

Every Platform code in one place. Each endpoint above lists the ones it can answer.

CodeStatusMeaning
ACCOUNT_NOT_ACTIVE409The user's account is closing, so it cannot withdraw
ALREADY_RATED409This side already rated this deal
BANK_ACCOUNT_COOLING_OFF409The bank account is in its cooling-off. See usable_after
BANK_ACCOUNT_EXISTS409This bank account is already saved for the user
BANK_ACCOUNT_LIMIT409The user has 10 payout bank accounts with you
BANK_ACCOUNT_NOT_FOUND404No such bank account for the user
CHECKOUT_CHANGED409Buyer page: the order changed after the checkout link was made
CHECKOUT_USED409Buyer page: this checkout link already started a payment
CONSENT_REQUIRED422Showing unclaimed profiles needs the consent statement ticked
DAILY_CAP_REACHED409The deal would pass your daily cap
DEAL_CREATE_BUSY409Another deal is being created at the same moment
DUPLICATE_ORDER_REF409A deal with this partner_order_ref exists
EVIDENCE_LIMIT_REACHED409The dispute holds 50 files
EXTEND_DISABLED409Extensions are switched off
EXTEND_LIMIT_REACHED409The milestone was extended the most times allowed
FEE_EXCEEDS_AMOUNT422Deals: a milestone is too small for the seller's fee share. Withdrawals: the fee is not lower than the amount
FILE_TOO_LARGE413A file is over 10 MB
INSUFFICIENT_BALANCE422The balance is too low for the withdrawal
INVALID_AMOUNT422The withdrawal amount is outside the limits
INVALID_BODY400The body is missing a field or a field is not valid
INVALID_FEE_ALLOCATION400fee_bearer and fee_split_buyer_pct do not fit together
INVALID_NIK400kyc_nik is not a valid NIK
INVALID_QUERY400A list query value is not valid
INVALID_STATE409The deal or milestone is not in a status that allows this
KYB_REQUIRED403A Live request of a workspace without verified KYB
KYC_LOCKED409A KYC is already pending or verified
KYC_NOT_PENDING409Sandbox: no pending KYC to simulate
KYC_SUBMIT_LIMITED429Too many KYC submissions for now
LIMIT_EXCEEDED422The buyer or seller is over the non-KYC limits (party, reason)
LINK_EXPIRED410Buyer and user pages: the link is invalid, expired or used
MAIL_LIMIT_REACHED429Live: this user or address already got as many emails as allowed today (Retry-After)
NAME_LOCKED409The user's name can no longer be changed through the API
NAME_MISMATCH422A bank holder name or KYC name does not match the user's name
NIK_IN_USE409The NIK verifies another RekberPay account
NO_ACTIVE_TIMER409The milestone has no running automatic release
NOT_DISPUTED409The dispute is not open
NOT_EXTENDABLE409Only a shipped milestone can be extended
NOT_RATEABLE409The deal is not completed, so it cannot be rated yet (or every paid part was refunded)
PAYMENT_IN_PROGRESS409A checkout payment can still land on the deal
PLATFORM_FROZEN423RekberPay paused your Platform access
REF_IN_USE409The partner_user_ref is linked to another email
RISK_BLOCKED403RekberPay screening refused the withdrawal
SAME_PARTY422The buyer and the seller are the same person
SANDBOX_EMAIL_REQUIRED422A sandbox user needs a test domain email
SANDBOX_MAIN_BALANCE422Sandbox withdrawals come from the partner balance only
SANDBOX_TEST_NIK_REQUIRED422Sandbox KYC needs a test NIK that ends in 0000
SCOPE_NOT_ALLOWED400Creating a key with a Platform scope your terms do not allow
SESSION_REQUIRED403This setting is changed in the dashboard only, never with an API key
TEST_DEAL409Buyer page: a sandbox order cannot take a real payment
TOO_MANY_CODES429Claim page: too many codes were requested
TRY_AGAIN409Another change to the same user was saved at the same moment. Nothing was done
TYPE_NOT_ALLOWED400The file type is not allowed
TYPE_MISMATCH, IMAGE_DECODE_FAILED, PDF_*400The file content is not what it claims, is damaged, or a PDF cannot be cleaned
QUOTA_EXCEEDED429The daily upload limit is reached
STORAGE_FAILED500A file could not be stored. Nothing changed
PDF_CHECK_UNAVAILABLE503PDFs cannot be checked right now
WITHDRAWAL_FAILED500The withdrawal could not be saved. Nothing was charged
USER_ALREADY_LINKED409The person is linked to you under another ref
USER_DECLINED_RECENTLY429Live: the person declined or ended your link less than 7 days ago (Retry-After)
USER_MANAGED_BY_OWNER409The person manages their own account
USER_NOT_AVAILABLE422 or 409The person cannot take part, or RekberPay ended your link (422), or a buyer is no longer linked (409)
USER_NOT_FOUND404Deal create: no active user with this ref

The codes every route can answer are in Errors every Platform route can answer.

Escrow, payment services and B2B deals for Indonesian businesses.