Skip to content

Enterprise approvals and payouts ​

After evidence is submitted, finance members decide on it. A milestone at or above the four-eyes threshold needs two different people to approve. An approved milestone is then released by finance to one to five verified payout instructions; each split becomes a payout (disbursement) that RekberPay settles. Money only ever leaves to a payout instruction that RekberPay has verified and signed. Auth, roles, the workspace header and the shared limits are explained in Enterprise overview.

Endpoints ​

MethodPathEndpointAuth
GET/v1/enterprise/approvalsList the approval queueviewer
GET/v1/enterprise/approvals/:milestoneIdGet an approvalviewer
POST/v1/enterprise/approvals/:milestoneId/decideDecide on evidencefinance
POST/v1/enterprise/deals/:id/milestones/:mid/releaseRelease a milestonefinance
GET/v1/enterprise/payoutsList payoutsviewer
GET/v1/enterprise/payout-instructionsList payout instructionsviewer
GET/v1/enterprise/payout-instructions/:idGet a payout instructionviewer
POST/v1/enterprise/payout-instructionsAdd a payout instructionfinance
PATCH/v1/enterprise/payout-instructions/:idUpdate a payout instructionfinance
DELETE/v1/enterprise/payout-instructions/:idDelete a payout instructionfinance

List the approval queue ​

GET/v1/enterprise/approvals

Milestones waiting for a decision, oldest first. With status=decided, milestones decided in the last 30 days, newest first, with their current status.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: Any workspace member (viewer and up).
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Query parameters ​

NameTypeRequiredDescription
statusstringNodecided for recent decisions. Leave it out for the open queue
pageintegerNoDefault 1
limitintegerNo1 to 50. Default 20

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/approvals?limit=2" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/approvals?limit=2', {
    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/approvals?limit=2",
    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/approvals?limit=2');
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": [
        {
            "milestone_id": "aaaaaaa5-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
            "deal_id": "bbbbbbb5-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
            "deal_reference": "DEAL-2026-0003",
            "deal_title": "Electronic control boards, lot 7",
            "milestone_title": "Goods in transit",
            "status": "evidence_submitted",
            "amount": 150000000,
            "currency": "IDR",
            "counterparty_name": "Harbourline Components Pte Ltd",
            "risk_tier": "standard",
            "evidence_summary": "Bill of lading HLCU-5521 and the export declaration for all 600 boards.",
            "evidence_submitted_at": "2026-10-13T09:33:06.162Z",
            "due_at": "2026-10-15T14:33:06.162Z",
            "approvals_required": 2,
            "approvals_done": 1,
            "my_decision": "approved",
            "submitted_by": {
                "id": "88888884-8888-4888-8888-888888888888",
                "name": "Sari Wibowo"
            }
        },
        {
            "milestone_id": "ccccccc5-cccc-4ccc-8ccc-cccccccccccc",
            "deal_id": "ddddddd5-dddd-4ddd-8ddd-dddddddddddd",
            "deal_reference": "DEAL-2026-0004",
            "deal_title": "Cold-chain logistics, Jakarta to Surabaya",
            "milestone_title": "QA inspection passed",
            "status": "evidence_submitted",
            "amount": 18500000,
            "currency": "IDR",
            "counterparty_name": "PT Sinar Logistik Nusantara",
            "risk_tier": "standard",
            "evidence_summary": "Temperature log for the full route, never warmer than -19C.",
            "evidence_submitted_at": "2026-10-13T12:33:06.162Z",
            "due_at": "2026-10-16T02:33:06.162Z",
            "approvals_required": 1,
            "approvals_done": 0,
            "my_decision": null,
            "submitted_by": {
                "id": "88888884-8888-4888-8888-888888888888",
                "name": "Sari Wibowo"
            }
        }
    ],
    "pagination": {
        "page": 1,
        "limit": 2,
        "total": 4,
        "total_pages": 2
    }
}
FieldTypeDescription
approvals_requiredinteger1, or 2 for four-eyes milestones
approvals_doneintegerApprovals given in the current round
my_decisionstring | nullYour decision in this round, if any
submitted_byobjectWho submitted the evidence. That person cannot decide

Errors ​

StatusCodeCause
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Get an approval ​

GET/v1/enterprise/approvals/:milestoneId

Everything needed to decide: the queue item, the proposed payout with a masked account number, recent events (how_we_got_here), impact, the current round under decision and the evidence documents.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: Any workspace member (viewer and up).
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Path parameters ​

NameTypeDescription
milestoneIdUUIDThe milestone ID

Example request ​

bash
curl https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff', {
    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/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff",
    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/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff');
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": {
        "item": {
            "milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
            "deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
            "deal_reference": "DEAL-2026-0009",
            "deal_title": "Packaging run Q4",
            "milestone_title": "Tooling and proof approval",
            "status": "evidence_submitted",
            "amount": 30000000,
            "currency": "IDR",
            "counterparty_name": "Harbourline Components Pte Ltd",
            "risk_tier": "standard",
            "evidence_summary": "Proof sheet signed by both sides on 10 October.",
            "evidence_submitted_at": "2026-10-13T14:33:16.429Z",
            "due_at": null,
            "approvals_required": 1,
            "approvals_done": 0,
            "my_decision": null,
            "submitted_by": {
                "id": "88888884-8888-4888-8888-888888888888",
                "name": "Sari Wibowo"
            }
        },
        "proposed": {
            "amount": 30000000,
            "currency": "IDR",
            "destination": {
                "label": "Harbourline IDR collection account",
                "bank_name": "DBS Bank Indonesia",
                "account_number_masked": "3320****43"
            },
            "deal_progress_pct": 0
        },
        "how_we_got_here": [
            {
                "seq": 140528,
                "event": "deal.created",
                "entity_type": "deal",
                "entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
                "actor": {
                    "id": "33333333-3333-4333-8333-333333333333",
                    "name": "Dimas Hidayat"
                },
                "amount": 50000000,
                "summary": "Created deal DEAL-2026-0009: Packaging run Q4",
                "created_at": "2026-10-13T14:33:16.361Z"
            },
            {
                "seq": 140529,
                "event": "deal.updated",
                "entity_type": "deal",
                "entity_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
                "actor": {
                    "id": "33333333-3333-4333-8333-333333333333",
                    "name": "Dimas Hidayat"
                },
                "amount": null,
                "summary": "Updated deal DEAL-2026-0009",
                "created_at": "2026-10-13T14:33:16.369Z"
            }
        ],
        "impact": {
            "counterparty_paid": 30000000,
            "money_moved": 30000000,
            "milestones_remaining": 2,
            "deal_progress_after_pct": 50
        },
        "decision": {
            "risk_tier": "standard",
            "approvals_required": 1,
            "approvals": [
                {
                    "id": "eeeeeee5-eeee-4eee-8eee-eeeeeeeeeeee",
                    "approver": null,
                    "status": "pending",
                    "note": null,
                    "decided_at": null
                }
            ],
            "reversibility": "A decision is final once saved. Release is a separate finance step.",
            "expires_at": null
        },
        "evidence": [
            {
                "id": "88888885-8888-4888-8888-888888888888",
                "title": "Signed proof sheet",
                "document_type": "inspection",
                "file_url": "https://files.example.com/northline/proof-sheet-q4.pdf",
                "checksum_sha256": "a3f1c2d4e5b60718293a4b5c6d7e8f90112233445566778899aabbccddeeff00",
                "visibility": "account",
                "uploaded_by": "33333333-3333-4333-8333-333333333333",
                "created_at": "2026-10-13T14:33:16.424Z"
            }
        ]
    }
}

Lists are cut to the first 2 items and long text is shortened here.

Errors ​

StatusCodeCause
404NOT_FOUNDNo such milestone in the workspace
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Decide on evidence ​

POST/v1/enterprise/approvals/:milestoneId/decide

Approves the evidence, rejects it or asks for a revision. A decision is final once saved; release is a separate step. With four-eyes the first approval leaves the milestone in evidence_submitted with approvals_done: 1, and the second makes it approved. Rejecting or asking for a revision closes the round and moves the milestone to revision_requested.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: finance, admin or owner.
  • Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
  • Idempotency: Send an Idempotency-Key header (UUID v4). A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeDescription
milestoneIdUUIDThe milestone ID

Body (JSON) ​

NameTypeRequiredDescription
decisionstringYesapproved, rejected or revision_requested
notestringNoAt most 700 characters. Required, at least 5 characters, unless the decision is approved

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: fffffff5-ffff-4fff-8fff-ffffffffffff" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "approved",
    "note": "Proof matches the spec sheet."
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': 'fffffff5-ffff-4fff-8fff-ffffffffffff',
    },
    body: JSON.stringify({
        decision: "approved",
        note: "Proof matches the spec sheet."
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "fffffff5-ffff-4fff-8fff-ffffffffffff",
    },
    json={
        "decision": "approved",
        "note": "Proof matches the spec sheet.",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/approvals/fffffff4-ffff-4fff-8fff-ffffffffffff/decide');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: fffffff5-ffff-4fff-8fff-ffffffffffff',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'decision' => 'approved',
        'note' => 'Proof matches the spec sheet.',
    ]),
]);
$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": {
        "milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
        "approval_id": "eeeeeee5-eeee-4eee-8eee-eeeeeeeeeeee",
        "decision": "approved",
        "milestone_status": "approved",
        "approvals_required": 1,
        "approvals_done": 1
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYDecision not valid, or a note is missing for a rejection or revision
404NOT_FOUNDNo such milestone
409NOT_AWAITING_APPROVALThe milestone is not evidence_submitted
409ALREADY_DECIDEDYou already decided in this round
409SUBMITTER_CANNOT_APPROVEYou submitted the evidence, so someone else must decide
409NO_PENDING_APPROVALEvery approval in this round is taken
409CONCURRENT_MODIFICATIONSomeone changed the milestone first. Read it again
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
403FORBIDDENYour role is below finance
403SCOPE_REQUIREDThe workspace key only has the read scope
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

A real SUBMITTER_CANNOT_APPROVE answer:

409 Conflict

json
{
    "success": false,
    "error": {
        "code": "SUBMITTER_CANNOT_APPROVE",
        "message": "The person who submitted the evidence cannot decide on it."
    }
}

Release a milestone ​

POST/v1/enterprise/deals/:id/milestones/:mid/release

Releases an approved milestone to one to five verified payout instructions. The split amounts must add up to the milestone payout_amount exactly: the milestone amount minus its share of the platform fee the counterparty pays (equal to the amount when your workspace pays the whole fee, see Enterprise platform fee). Each split becomes a payout in queued; follow it with List payouts or the event feed. Release is final. A payout that fails at the bank stays failed with a failure_reason until RekberPay queues it again; the milestone stays released.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: finance, admin or owner.
  • Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
  • Idempotency: Send an Idempotency-Key header (UUID v4). A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeDescription
idUUIDThe deal ID
midUUIDThe milestone ID

Body (JSON) ​

NameTypeRequiredDescription
splitsarrayYes1 to 5 items of { payout_instruction_id, amount }
splits[].payout_instruction_idUUIDYesA verified instruction in the deal currency, for the deal counterparty or workspace-wide
splits[].amountintegerYesWhole rupiah, more than 0

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/release \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 33333336-3333-4333-8333-333333333333" \
  -H "Content-Type: application/json" \
  -d '{
    "splits": [
      {
        "payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
        "amount": 25000000
      },
      {
        "payout_instruction_id": "22222224-2222-4222-8222-222222222222",
        "amount": 5000000
      }
    ]
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/release', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '33333336-3333-4333-8333-333333333333',
    },
    body: JSON.stringify({
        splits: [
            {
                payout_instruction_id: "fffffff3-ffff-4fff-8fff-ffffffffffff",
                amount: 25000000
            },
            {
                payout_instruction_id: "22222224-2222-4222-8222-222222222222",
                amount: 5000000
            }
        ]
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/release",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "33333336-3333-4333-8333-333333333333",
    },
    json={
        "splits": [
            {
                "payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
                "amount": 25000000,
            },
            {
                "payout_instruction_id": "22222224-2222-4222-8222-222222222222",
                "amount": 5000000,
            },
        ],
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/deals/eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee/milestones/fffffff4-ffff-4fff-8fff-ffffffffffff/release');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 33333336-3333-4333-8333-333333333333',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'splits' => [
            [
                'payout_instruction_id' => 'fffffff3-ffff-4fff-8fff-ffffffffffff',
                'amount' => 25000000,
            ],
            [
                'payout_instruction_id' => '22222224-2222-4222-8222-222222222222',
                'amount' => 5000000,
            ],
        ],
    ]),
]);
$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": {
        "milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
        "status": "released",
        "deal_status": "in_progress",
        "disbursements": [
            {
                "id": "44444446-4444-4444-8444-444444444444",
                "payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
                "amount": 25000000,
                "currency": "IDR",
                "status": "queued"
            },
            {
                "id": "55555556-5555-4555-8555-555555555555",
                "payout_instruction_id": "22222224-2222-4222-8222-222222222222",
                "amount": 5000000,
                "currency": "IDR",
                "status": "queued"
            }
        ]
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYSplits missing, empty or more than 5
400SPLITS_MISMATCHThe splits do not add up to the milestone payout_amount (the amount minus the counterparty fee share)
400INSTRUCTION_NOT_ELIGIBLEAn instruction is not verified, not in the deal currency or for another counterparty
404NOT_FOUNDNo such deal or milestone
409NOT_APPROVEDOnly an approved milestone can be released
409TEST_DEALThe deal was made in the sandbox with test money and the workspace is now live. It never releases real payouts
409LIVE_DEAL_IN_SANDBOXA live deal cannot release while the workspace is in the sandbox, and a live milestone funded with sandbox test money never releases real payouts
409PAYOUT_INSTRUCTION_TAMPEREDAn instruction failed its signature check. Contact RekberPay
409CONCURRENT_MODIFICATIONThe milestone changed while the call ran
503SIGNING_NOT_CONFIGUREDPayout signing is not set up on the server
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
403FORBIDDENYour role is below finance
403SCOPE_REQUIREDThe workspace key only has the read scope
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

A real SPLITS_MISMATCH answer:

400 Bad Request

json
{
    "success": false,
    "error": {
        "code": "SPLITS_MISMATCH",
        "message": "Splits must add up to the milestone amount."
    }
}

List payouts ​

GET/v1/enterprise/payouts

Payouts (disbursements) newest first, with the deal, milestone and masked destination, plus a summary of the queue.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: Any workspace member (viewer and up).
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Query parameters ​

NameTypeRequiredDescription
statusstringNoqueued, processing, settled or failed
pageintegerNoDefault 1
limitintegerNo1 to 50. Default 20

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/payouts?limit=2" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/payouts?limit=2', {
    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/payouts?limit=2",
    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/payouts?limit=2');
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": {
        "items": [
            {
                "id": "44444446-4444-4444-8444-444444444444",
                "deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
                "deal_reference": "DEAL-2026-0009",
                "milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
                "milestone_title": "Tooling and proof approval",
                "payout_instruction_id": "fffffff3-ffff-4fff-8fff-ffffffffffff",
                "instruction_label": "Harbourline IDR collection account",
                "bank_name": "DBS Bank Indonesia",
                "account_number_masked": "3320****43",
                "amount": 25000000,
                "currency": "IDR",
                "status": "queued",
                "failure_reason": null,
                "settled_at": null,
                "created_at": "2026-10-13T14:33:16.480Z",
                "updated_at": "2026-10-13T14:33:16.480Z"
            },
            {
                "id": "55555556-5555-4555-8555-555555555555",
                "deal_id": "eeeeeee4-eeee-4eee-8eee-eeeeeeeeeeee",
                "deal_reference": "DEAL-2026-0009",
                "milestone_id": "fffffff4-ffff-4fff-8fff-ffffffffffff",
                "milestone_title": "Tooling and proof approval",
                "payout_instruction_id": "22222224-2222-4222-8222-222222222222",
                "instruction_label": "Harbourline freight and duties",
                "bank_name": "Bank Central Asia",
                "account_number_masked": "8730****55",
                "amount": 5000000,
                "currency": "IDR",
                "status": "queued",
                "failure_reason": null,
                "settled_at": null,
                "created_at": "2026-10-13T14:33:16.480Z",
                "updated_at": "2026-10-13T14:33:16.480Z"
            }
        ],
        "summary": {
            "today_count": 4,
            "today_amount": 133000000,
            "avg_settlement_minutes": 32,
            "queued_amount": 58000000
        }
    },
    "pagination": {
        "page": 1,
        "limit": 2,
        "total": 8,
        "total_pages": 4
    }
}

Errors ​

StatusCodeCause
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

List payout instructions ​

GET/v1/enterprise/payout-instructions

Bank destinations, newest first. The full account number and the signature are never returned.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: Any workspace member (viewer and up).
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Query parameters ​

NameTypeRequiredDescription
counterparty_idUUIDNoOnly this counterparty
statusstringNodraft, pending_review, verified or suspended
pageintegerNoDefault 1
limitintegerNo1 to 50. Default 20

Example request ​

bash
curl "https://api.rekberpay.com/v1/enterprise/payout-instructions?counterparty_id=eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee" \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/payout-instructions?counterparty_id=eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee', {
    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/payout-instructions?counterparty_id=eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
    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/payout-instructions?counterparty_id=eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee');
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": "fffffff3-ffff-4fff-8fff-ffffffffffff",
            "label": "Harbourline IDR collection account",
            "counterparty": {
                "id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
                "name": "Harbourline Components Pte Ltd"
            },
            "currency": "IDR",
            "bank_name": "DBS Bank Indonesia",
            "account_number_masked": "3320****43",
            "account_holder": "Harbourline Components Pte Ltd",
            "country_code": "ID",
            "status": "verified",
            "signed": true,
            "verified_at": "2026-09-30T07:33:06.162Z",
            "created_at": "2026-09-30T03:33:06.162Z",
            "updated_at": "2026-10-13T14:33:06.231Z"
        }
    ],
    "pagination": {
        "page": 1,
        "limit": 20,
        "total": 1,
        "total_pages": 1
    }
}

Errors ​

StatusCodeCause
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Get a payout instruction ​

GET/v1/enterprise/payout-instructions/:id
  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: Any workspace member (viewer and up).
  • Rate limit: 300 requests a minute per user, shared by every route.
  • Idempotency: Not needed. Reading has no side effects.

Path parameters ​

NameTypeDescription
idUUIDThe payout instruction ID

Example request ​

bash
curl https://api.rekberpay.com/v1/enterprise/payout-instructions/fffffff3-ffff-4fff-8fff-ffffffffffff \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/payout-instructions/fffffff3-ffff-4fff-8fff-ffffffffffff', {
    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/payout-instructions/fffffff3-ffff-4fff-8fff-ffffffffffff",
    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/payout-instructions/fffffff3-ffff-4fff-8fff-ffffffffffff');
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": "fffffff3-ffff-4fff-8fff-ffffffffffff",
        "label": "Harbourline IDR collection account",
        "counterparty": {
            "id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
            "name": "Harbourline Components Pte Ltd"
        },
        "currency": "IDR",
        "bank_name": "DBS Bank Indonesia",
        "account_number_masked": "3320****43",
        "account_holder": "Harbourline Components Pte Ltd",
        "country_code": "ID",
        "status": "verified",
        "signed": true,
        "verified_at": "2026-09-30T07:33:06.162Z",
        "created_at": "2026-09-30T03:33:06.162Z",
        "updated_at": "2026-10-13T14:33:06.231Z"
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo such instruction in the workspace
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Add a payout instruction ​

POST/v1/enterprise/payout-instructions

Adds a bank destination. The server signs it and it starts as pending_review; RekberPay verifies it before it can receive money. In the sandbox it starts as a verified test instruction that only ever receives test payouts, and it is suspended when the workspace goes live. Spaces and dashes in account_number are dropped. The answer already masks the number.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: finance, admin or owner.
  • Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
  • 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
labelstringYes2 to 120 characters
currencystringYesThree letters, for example IDR
bank_namestringYes2 to 100 characters
account_numberstringYes6 to 80 letters or digits after spaces and dashes are removed
account_holderstringYes2 to 160 characters
country_codestringYesTwo letters
counterparty_idUUID | nullNoTie it to one counterparty. Leave it out for a workspace-wide destination

Example request ​

bash
curl -X POST https://api.rekberpay.com/v1/enterprise/payout-instructions \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 11111114-1111-4111-8111-111111111111" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Harbourline freight account",
    "counterparty_id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
    "currency": "IDR",
    "bank_name": "Bank Central Asia",
    "account_number": "8730 0412 55",
    "account_holder": "PT Harbourline Indonesia",
    "country_code": "ID"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/payout-instructions', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '11111114-1111-4111-8111-111111111111',
    },
    body: JSON.stringify({
        label: "Harbourline freight account",
        counterparty_id: "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
        currency: "IDR",
        bank_name: "Bank Central Asia",
        account_number: "8730 0412 55",
        account_holder: "PT Harbourline Indonesia",
        country_code: "ID"
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.post(
    "https://api.rekberpay.com/v1/enterprise/payout-instructions",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "11111114-1111-4111-8111-111111111111",
    },
    json={
        "label": "Harbourline freight account",
        "counterparty_id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
        "currency": "IDR",
        "bank_name": "Bank Central Asia",
        "account_number": "8730 0412 55",
        "account_holder": "PT Harbourline Indonesia",
        "country_code": "ID",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/payout-instructions');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 11111114-1111-4111-8111-111111111111',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'label' => 'Harbourline freight account',
        'counterparty_id' => 'eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee',
        'currency' => 'IDR',
        'bank_name' => 'Bank Central Asia',
        'account_number' => '8730 0412 55',
        'account_holder' => 'PT Harbourline Indonesia',
        'country_code' => 'ID',
    ]),
]);
$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": "22222224-2222-4222-8222-222222222222",
        "label": "Harbourline freight account",
        "counterparty": {
            "id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
            "name": "Harbourline Components Pte Ltd"
        },
        "currency": "IDR",
        "bank_name": "Bank Central Asia",
        "account_number_masked": "8730****55",
        "account_holder": "PT Harbourline Indonesia",
        "country_code": "ID",
        "status": "pending_review",
        "signed": true,
        "verified_at": null,
        "created_at": "2026-10-13T14:33:16.301Z",
        "updated_at": "2026-10-13T14:33:16.301Z"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYA field failed validation
404COUNTERPARTY_NOT_FOUNDThe counterparty is not in this workspace
503SIGNING_NOT_CONFIGUREDPayout signing is not set up on the server
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
403FORBIDDENYour role is below finance
403SCOPE_REQUIREDThe workspace key only has the read scope
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Update a payout instruction ​

PATCH/v1/enterprise/payout-instructions/:id

Any of the create fields. Changing a bank field or the counterparty signs the instruction again and sends it back to pending_review. A label change keeps its status.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: finance, admin or owner.
  • Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
  • Idempotency: Send an Idempotency-Key header (UUID v4). A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeDescription
idUUIDThe payout instruction ID

Example request ​

bash
curl -X PATCH https://api.rekberpay.com/v1/enterprise/payout-instructions/22222224-2222-4222-8222-222222222222 \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 33333334-3333-4333-8333-333333333333" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Harbourline freight and duties"
  }'
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/payout-instructions/22222224-2222-4222-8222-222222222222', {
    method: 'PATCH',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': '33333334-3333-4333-8333-333333333333',
    },
    body: JSON.stringify({
        label: "Harbourline freight and duties"
    }),
});
const data = await res.json();
python
import os

import requests

resp = requests.patch(
    "https://api.rekberpay.com/v1/enterprise/payout-instructions/22222224-2222-4222-8222-222222222222",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "33333334-3333-4333-8333-333333333333",
    },
    json={
        "label": "Harbourline freight and duties",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/payout-instructions/22222224-2222-4222-8222-222222222222');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: 33333334-3333-4333-8333-333333333333',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'label' => 'Harbourline freight and duties',
    ]),
]);
$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": "22222224-2222-4222-8222-222222222222",
        "label": "Harbourline freight and duties",
        "counterparty": {
            "id": "eeeeeee3-eeee-4eee-8eee-eeeeeeeeeeee",
            "name": "Harbourline Components Pte Ltd"
        },
        "currency": "IDR",
        "bank_name": "Bank Central Asia",
        "account_number_masked": "8730****55",
        "account_holder": "PT Harbourline Indonesia",
        "country_code": "ID",
        "status": "pending_review",
        "signed": true,
        "verified_at": null,
        "created_at": "2026-10-13T14:33:16.301Z",
        "updated_at": "2026-10-13T14:33:16.309Z"
    }
}

Errors ​

StatusCodeCause
400INVALID_BODYA field failed validation, or nothing to update
404NOT_FOUND, COUNTERPARTY_NOT_FOUNDNo such instruction, or the counterparty is not in this workspace
409SUSPENDEDThe instruction is suspended. Contact RekberPay
409IN_USEA payout to it is queued or processing
409PAYOUT_INSTRUCTION_TAMPEREDThe stored details no longer match their signature
503SIGNING_NOT_CONFIGUREDPayout signing is not set up on the server
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
403FORBIDDENYour role is below finance
403SCOPE_REQUIREDThe workspace key only has the read scope
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Delete a payout instruction ​

DELETE/v1/enterprise/payout-instructions/:id

Deletes an instruction that never received a payout.

  • Auth: A workspace API key (Authorization: Bearer rpe_...), a personal API key or a dashboard session.
  • Role: finance, admin or owner.
  • Rate limit: 30 a minute per user for this route, plus the shared 300 a minute.
  • Idempotency: Send an Idempotency-Key header (UUID v4). A retry with the same key and body returns the first answer with Idempotent-Replay: true.

Path parameters ​

NameTypeDescription
idUUIDThe payout instruction ID

Example request ​

bash
curl -X DELETE https://api.rekberpay.com/v1/enterprise/payout-instructions/44444444-4444-4444-8444-444444444444 \
  -H "Authorization: Bearer $REKBERPAY_WORKSPACE_KEY" \
  -H "Idempotency-Key: 55555554-5555-4555-8555-555555555555"
js
const res = await fetch('https://api.rekberpay.com/v1/enterprise/payout-instructions/44444444-4444-4444-8444-444444444444', {
    method: 'DELETE',
    headers: {
        Authorization: `Bearer ${process.env.REKBERPAY_WORKSPACE_KEY}`,
        'Idempotency-Key': '55555554-5555-4555-8555-555555555555',
    },
});
const data = await res.json();
python
import os

import requests

resp = requests.delete(
    "https://api.rekberpay.com/v1/enterprise/payout-instructions/44444444-4444-4444-8444-444444444444",
    headers={
        "Authorization": f"Bearer {os.environ['REKBERPAY_WORKSPACE_KEY']}",
        "Idempotency-Key": "55555554-5555-4555-8555-555555555555",
    },
    timeout=30,
)
print(resp.status_code, resp.json())
php
<?php

$ch = curl_init('https://api.rekberpay.com/v1/enterprise/payout-instructions/44444444-4444-4444-8444-444444444444');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('REKBERPAY_WORKSPACE_KEY'),
        'Idempotency-Key: 55555554-5555-4555-8555-555555555555',
    ],
]);
$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": "44444444-4444-4444-8444-444444444444",
        "deleted": true
    }
}

Errors ​

StatusCodeCause
404NOT_FOUNDNo such instruction
409IN_USEThe instruction has payouts
401UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKENNo session or key, the session expired, or the key is wrong or revoked
400INVALID_ACCOUNTX-Enterprise-Account is not a UUID
403NO_WORKSPACEYou are not an active member of that workspace, or the key belongs to another one
403FORBIDDENYour role is below finance
403SCOPE_REQUIREDThe workspace key only has the read scope
403CSRF_FAILEDSession call without the matching X-CSRF-Token header
429RATE_LIMITEDOver the rate limit. Wait for Retry-After seconds

Escrow, payment services and B2B deals for Indonesian businesses.