Skip to content

Withdrawals API ​

When an escrow is released, the seller's share lands in their RekberPay balance. A withdrawal moves it out to a bank account or an e-wallet. The amount is taken from the balance when the request is made; RekberPay then pays it out and the status moves on from pending.

Endpoints ​

MethodPathEndpointAuth
GET/v1/user/balanceGet the balanceKey or session
GET/v1/withdrawals/configGet withdrawal limitsKey or session
POST/v1/withdrawalsRequest a withdrawalKey or session
GET/v1/withdrawalsList withdrawalsKey or session

Get the balance ​

GET/v1/user/balance

The current balance with recent balance movements, recent withdrawals and 30-day totals.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Example request ​

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

import requests

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

$ch = curl_init('https://api.rekberpay.com/v1/user/balance');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_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": {
        "balance": 242500,
        "recent_transactions": [
            {
                "id": "22222222-2222-4222-8222-222222222222",
                "type": "release_to_seller",
                "amount": "242500.00",
                "currency": "IDR",
                "status": "success",
                "created_at": "2026-10-13T14:23:18.757Z",
                "escrow_id": "33333330-3333-4333-8333-333333333333",
                "escrow_amount": "250000.00",
                "platform_fee": "7500.00"
            }
        ],
        "recent_withdrawals": [],
        "stats_30d": {
            "credit": 242500,
            "debit": 0,
            "pending_count": 0
        }
    }
}

Errors ​

StatusCodeCause
404USER_NOT_FOUNDThe account was removed
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Get withdrawal limits ​

GET/v1/withdrawals/config

The minimum, maximum and step for an amount, the fee rule, your loyalty discount on the fee and your unused withdrawal fee vouchers. The fee rule here is the one POST /v1/withdrawals charges with. If RekberPay's fee settings are missing or invalid, both fall back to a fixed Rp5.000 with a Rp2.500 floor and a Rp25.000 cap.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Example request ​

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

import requests

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

$ch = curl_init('https://api.rekberpay.com/v1/withdrawals/config');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_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": {
        "withdraw_min_idr": 5000,
        "withdraw_max_idr": 50000000,
        "withdraw_step_idr": 100,
        "withdraw_fee_type": "percentage",
        "withdraw_fee_value": 1,
        "withdraw_fee_min_idr": 1500,
        "withdraw_fee_max_idr": 25000000,
        "withdraw_fee_loyalty_discount_percent": 0,
        "withdraw_fee_vouchers": []
    }
}
FieldTypeDescription
withdraw_fee_type, withdraw_fee_valuestring, numberpercentage or fixed, and its value
withdraw_fee_min_idr, withdraw_fee_max_idrintegerFloor and cap of the fee. A cap of 0 means withdrawals are free
withdraw_fee_loyalty_discount_percentnumberYour loyalty level discount on the fee, 0 to 100
withdraw_fee_vouchersarrayYour unused one-time withdrawal fee vouchers: redemption_id, voucher_id, code, percent_off, amount_off, max_discount_amount, expires_at

The fee charged is the rule above, then the loyalty discount, then the voucher that takes off the most (ties go to the one that expires first). A voucher is used up by the withdrawal and comes back if RekberPay rejects it.

Errors ​

StatusCodeCause
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Request a withdrawal ​

POST/v1/withdrawals

Takes the amount from the balance and queues the payout. The amount must be a multiple of withdraw_step_idr, unless you withdraw the whole balance. Check the destination carefully: a payout to a mistyped account cannot be pulled back. Send destination_ack: true once the user confirmed it.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Send an Idempotency-Key header (UUID v4). A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Body (JSON) ​

NameTypeRequiredDescription
amountnumberYesRupiah, between withdraw_min_idr and withdraw_max_idr
methodstringYesbank_transfer, dana, gopay or crypto
destination_detailsobjectYesFor bank_transfer: bank_name, account_number, account_holder. For dana and gopay: phone. For crypto: network, wallet_address
destination_ackbooleanNotrue records that the user confirmed this exact destination

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/withdrawals \
  -H "Authorization: Bearer $REKBERPAY_API_KEY" \
  -H "Idempotency-Key: 33333332-3333-4333-8333-333333333333" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100000,
    "method": "bank_transfer",
    "destination_details": {
      "bank_name": "BCA",
      "account_number": "1234567890",
      "account_holder": "Budi Santoso"
    },
    "destination_ack": true
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/withdrawals', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_API_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '33333332-3333-4333-8333-333333333333',
    },
    body: JSON.stringify({
        amount: 100000,
        method: "bank_transfer",
        destination_details: {
            bank_name: "BCA",
            account_number: "1234567890",
            account_holder: "Budi Santoso"
        },
        destination_ack: true
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/withdrawals",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_API_KEY']}",
        "Idempotency-Key": "33333332-3333-4333-8333-333333333333",
    },
    json={
        "amount": 100000,
        "method": "bank_transfer",
        "destination_details": {
            "bank_name": "BCA",
            "account_number": "1234567890",
            "account_holder": "Budi Santoso",
        },
        "destination_ack": True,
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/withdrawals');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 33333332-3333-4333-8333-333333333333',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 100000,
        'method' => 'bank_transfer',
        'destination_details' => [
            'bank_name' => 'BCA',
            'account_number' => '1234567890',
            'account_holder' => 'Budi Santoso',
        ],
        'destination_ack' => true,
    ]),
]);
$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": "44444442-4444-4444-8444-444444444444",
        "user_id": "44444440-4444-4444-8444-444444444444",
        "amount": "100000.00",
        "fee_amount": "1500.00",
        "net_amount": "98500.00",
        "method": "bank_transfer",
        "destination_details": "{\"bank_name\":\"BCA\",\"account_number\":\"1234567890\",\"account_holder\":\"Budi Santoso\"}",
        "status": "pending",
        "admin_note": null,
        "processed_by": null,
        "processed_at": null,
        "created_at": "2026-10-13T14:23:18.920Z",
        "updated_at": "2026-10-13T14:23:18.920Z",
        "risk": {
            "action": "allow",
            "flags": []
        }
    }
}
FieldTypeDescription
amountstringTaken from the balance
fee_amountstringWithdrawal fee, after your loyalty discount and fee voucher
net_amountstringWhat arrives at the destination
statusstringpending until RekberPay pays it out

Errors ​

StatusCodeCause
400INVALID_AMOUNTBelow the minimum, above the maximum, or not a multiple of the step
400INVALID_METHODmethod is not one of the four
400MISSING_DESTINATIONdestination_details is missing
400FEE_EXCEEDS_AMOUNTThe fee is larger than the amount
400INSUFFICIENT_BALANCEThe balance is too low
403EMAIL_NOT_VERIFIEDYou signed up with email and password and have not confirmed the email yet
403RISK_BLOCKEDThe withdrawal was stopped by a risk check. Contact support
404USER_NOT_FOUNDThe account was removed
500DB_ERRORThe withdrawal was not created. Retry with the same Idempotency-Key
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

A real INVALID_AMOUNT answer:

400 Bad Request

json
{
    "success": false,
    "error": {
        "code": "INVALID_AMOUNT",
        "message": "Minimum withdraw adalah Rp 5.000"
    }
}

List withdrawals ​

GET/v1/withdrawals

Your withdrawals, newest first.

  • Auth: A personal API key (Authorization: Bearer <key>) or a session cookie with X-CSRF-Token on writes.
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Query parameters ​

NameTypeRequiredDescription
pageintegerNoDefault 1
limitintegerNo1 to 50. Default 20

Example request ​

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

import requests

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

$ch = curl_init('https://api.rekberpay.com/v1/withdrawals?limit=5');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_API_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": "44444442-4444-4444-8444-444444444444",
            "user_id": "44444440-4444-4444-8444-444444444444",
            "amount": "100000.00",
            "fee_amount": "1500.00",
            "net_amount": "98500.00",
            "method": "bank_transfer",
            "destination_details": "{\"bank_name\":\"BCA\",\"account_number\":\"1234567890\",\"account_holder\":\"Budi Santoso\"}",
            "status": "pending",
            "admin_note": null,
            "processed_by": null,
            "processed_at": null,
            "created_at": "2026-10-13T14:23:18.920Z",
            "updated_at": "2026-10-13T14:23:18.920Z"
        }
    ],
    "pagination": {
        "page": 1,
        "limit": 5,
        "total": "1"
    }
}

Errors ​

StatusCodeCause
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or API key, the session expired, or the key is wrong or revoked
401WORKSPACE_KEY_ONLYA workspace key (rpe_...) was sent. Those only work under /v1/enterprise
403ACCOUNT_INACTIVE, SUSPENDEDThe account is closed, being deleted or suspended
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Escrow, payment services and B2B deals for Indonesian businesses.