Approve, release and settle
Once a milestone is delivered, three things stand between the money and the counterparty: an approval, a release and a settlement. They are separate on purpose. The person who approves is not the person who delivered the milestone, and the bank account that receives the money is one RekberPay already verified and signed.
1. Approve
An approver is anyone with the finance role or above, except the person who delivered the milestone.
In the dashboard
- Open Approvals. The Pending tab lists milestones waiting for a decision. A badge on the menu counts them.
- Open one. Read the delivery summary, the documents and the requirements.
- Choose Approve, Reject or Request revision. Reject and Request revision need a note of at least 5 characters, which the person who delivered will read.
Approving records your decision. It does not move money. Reject and Request revision both send the milestone back to revision_requested, close the round, and let the milestone be delivered again, which starts a new round.
With the API
curl -X POST https://api.rekberpay.com/v1/enterprise/approvals/$MILESTONE_ID/decide \
-H "Authorization: Bearer $REKBERPAY_APPROVER_KEY" \
-H "Idempotency-Key: $(uuidgen | tr 'A-Z' 'a-z')" \
-H "Content-Type: application/json" \
-d '{ "decision": "approved", "note": "Matches the order confirmation." }'decision is approved, rejected or revision_requested.
Four-eyes
A milestone with an amount at or above the workspace's four-eyes threshold needs two approvals from two different people. Below it, one is enough. The default threshold is Rp 100.000.000.
An admin changes it in Settings, Approval policy, Four-eyes threshold (IDR). The same card sets the Dispute SLA (hours).
curl -X PATCH https://api.rekberpay.com/v1/enterprise/settings \
-b cookies.txt \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H "X-Enterprise-Account: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{ "four_eyes_threshold": 50000000 }'Settings are changed from a dashboard session. A workspace API key can read them but never change them.
| Rule | Error |
|---|---|
Only finance, admin and owner can decide | 403 FORBIDDEN |
| The person who delivered the milestone cannot decide | 409 SUBMITTER_CANNOT_APPROVE |
| One decision per person per round | 409 ALREADY_DECIDED |
| The milestone must be waiting for a decision | 409 NOT_AWAITING_APPROVAL |
Because a workspace API key acts as the admin who created it, a key and that admin count as the same person. Give each approver their own login. The full walkthrough is in Four-eyes approvals.
When a milestone belongs to a deal with a linked partner, the partner delivers the milestone from its own workspace. Nobody in the payer's workspace is the submitter, so any eligible approver there can decide.
2. Keep verified payout instructions
A payout instruction is a bank destination for money you release. Releases go only to instructions that RekberPay has verified.
In the dashboard
- Open Payouts and the Instructions tab.
- Choose New payout instruction.
- Fill in Label, Counterparty, Bank, Account number, Account holder, Country code and Currency. Leave the counterparty empty to make the account usable for any deal of your workspace.
- Save. A live instruction shows
pending_reviewuntil RekberPay verifies it. A sandbox instruction is verified at once.
With the API
curl -X POST https://api.rekberpay.com/v1/enterprise/payout-instructions \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: $(uuidgen | tr 'A-Z' 'a-z')" \
-H "Content-Type: application/json" \
-d '{
"label": "Mitra Pangan main account",
"counterparty_id": "'"$COUNTERPARTY_ID"'",
"currency": "IDR",
"bank_name": "BCA",
"account_number": "8001234567",
"account_holder": "CV Mitra Pangan Nusantara",
"country_code": "ID"
}'Account numbers are never returned in full. Answers carry account_number_masked and signed: true instead.
| Status | Meaning |
|---|---|
pending_review | Waiting for RekberPay to verify it. Cannot receive a release yet |
verified | Can receive a release |
suspended | RekberPay suspended it. Contact RekberPay. It cannot be edited |
Changing the bank, account number, holder, country, currency or counterparty of an instruction signs it again and sends it back to pending_review. Renaming the label changes nothing else. Those bank detail changes are refused while a payout to the instruction is queued or processing (409 IN_USE), and an instruction that has ever received a payout cannot be deleted.
How instructions are signed
When an instruction is created or changed, RekberPay signs it with HMAC-SHA256. The signature covers these eight fields: account_holder, account_id, account_number, bank_name, counterparty_id, country_code, currency and id. The signing key stays on RekberPay's servers.
Every release, and every retry of a failed payout, checks the signature again. If anyone changed the stored bank details outside the normal flow, the check fails and the release is refused with 409 PAYOUT_INSTRUCTION_TAMPERED. If you ever see that code, stop and contact RekberPay.
Including counterparty_id is what keeps a counterparty's account from being turned into a workspace-wide one behind your back. Instructions signed with the older format, which did not cover the counterparty, are accepted during a short transition while RekberPay re-signs them. You do not need to do anything.
3. Release
When the milestone is approved, a finance member releases it. A release pays one to five accounts.
In the dashboard
- On the deal, choose Release on the milestone. If you just approved it, you can choose Release now.
- In Pay to (up to 5 accounts), pick verified accounts and give each an amount. The total must equal the payout amount shown. The dialog tells you how much is left to allocate.
- Confirm. Release is final.
The payout amount is the milestone amount minus the partner's share of the platform fee. If the payer pays the whole fee, it equals the milestone amount.
A live release waits for the counterparty's business verification. You can fund and approve a milestone while the counterparty is unverified. The money stays held, Release is off with the note Waits for KYB, and the approval page says the payout waits. The deal shows a banner with Send verification link. Release opens once RekberPay approves the counterparty, or when the counterparty is a live, verified RekberPay workspace. Sandbox deals are not affected. See Verify a counterparty.
With the API
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/$DEAL_ID/milestones/$MILESTONE_ID/release \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: $(uuidgen | tr 'A-Z' 'a-z')" \
-H "Content-Type: application/json" \
-d '{
"splits": [
{ "payout_instruction_id": "'"$MAIN_ACCOUNT_ID"'", "amount": 20000000 },
{ "payout_instruction_id": "'"$FREIGHT_ACCOUNT_ID"'", "amount": 9760000 }
]
}'200 OK
{
"success": true,
"data": {
"milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
"status": "released",
"deal_status": "in_progress",
"seller_fee": 240000,
"payout_amount": 29760000,
"disbursements": [
{
"id": "55555551-5555-4555-8555-555555555551",
"payout_instruction_id": "44444441-4444-4444-8444-444444444441",
"amount": 20000000,
"currency": "IDR",
"status": "queued"
},
{
"id": "55555552-5555-4555-8555-555555555552",
"payout_instruction_id": "44444442-4444-4444-8444-444444444442",
"amount": 9760000,
"currency": "IDR",
"status": "queued"
}
]
}
}| Status | Code | Cause |
|---|---|---|
400 | SPLITS_MISMATCH | The splits do not add up to the payout amount |
400 | INSTRUCTION_NOT_ELIGIBLE | An instruction is not verified, not in the deal currency, or belongs to another counterparty |
409 | PAYEE_CHOSE_PAYOUT | The counterparty chose where it is paid. Release without splits |
409 | NOT_APPROVED | The milestone is not approved, for example a four-eyes milestone with one approval |
409 | TEST_DEAL | A test deal cannot release real money |
409 | LIVE_DEAL_IN_SANDBOX | A live deal cannot release from Sandbox, and a live milestone funded with sandbox money never releases real payouts. Switch to Live |
409 | COUNTERPARTY_KYB_REQUIRED | The counterparty has not completed business verification. Nothing is paid out and the money stays held. The error carries kyb_status and a verify_path |
409 | PAYOUT_INSTRUCTION_TAMPERED | An instruction failed its signature check. Contact RekberPay |
403 | FORBIDDEN | Your role is below finance |
Each account can appear once in a release. Releasing with split payouts shows a full run with real answers.
Release to the counterparty's chosen account
On a deal shared with a counterparty workspace, the counterparty can choose where it is paid (Share a deal with your counterparty). When it did, the deal shows payee_payout_chosen: true and you release with no splits: the whole payout goes to that account as one payout, and you never see its details. Send {} as the body. Sending splits anyway answers 409 PAYEE_CHOSE_PAYOUT.
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/$DEAL_ID/milestones/$MILESTONE_ID/release \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
-H "Idempotency-Key: $(uuidgen | tr 'A-Z' 'a-z')" \
-H "Content-Type: application/json" \
-d '{}'The chosen account is checked again at release: it must still be verified, in the deal mode and currency, at least 24 hours past its verification, and held in the name of the counterparty. If it no longer passes, release asks for your own splits as usual (400 INVALID_BODY when you send none). Nothing is paid to a stale choice.
4. Settlement
Each split becomes one disbursement. It moves through these statuses:
| Status | Meaning |
|---|---|
queued | Released and waiting for RekberPay operations |
processing | RekberPay is sending it to the bank |
settled | The money reached the account. settled_at is set and a bank reference is recorded |
failed | It did not go through. failure_reason says why |
In Sandbox every payout is settled immediately as test data and never reaches operations.
In Live RekberPay operations send the transfer and mark it settled. You are notified when a payout settles or fails. A failed payout stays failed until RekberPay queues it again, to the same account or to another verified instruction. The milestone stays released throughout, and your team does not need to release it again.
In the dashboard, open Payouts. The Queue tab lists every payout with its status, a status filter, and a summary of today's payouts, the amount still queued and the average settlement time. The deal page shows each milestone's payouts too.
With the API
curl "https://api.rekberpay.com/v1/enterprise/payouts?status=queued" \
-H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"Webhooks and the activity feed carry disbursement.queued, disbursement.processing, disbursement.settled, disbursement.failed and disbursement.requeued. Finance can export every payout as CSV with GET /v1/enterprise/reports/disbursements.csv.